在 Javascript 中从经典继承切换到原型继承:模式的改变
Switch from classical to prototypal inheritance in Javascript: Change of pattern
有了Java背景,当我切换到Javascript时,我(懒惰地)试图坚持我所知道的关于oop的东西,即经典继承。我正在开发一个网络应用程序(我制作的),并使用了这种继承。但是,我正在考虑更改我的代码并重写 OOP 部分以进行原型继承(两个原因:我读了很多它更好,其次,我需要尝试另一种方式以便更好地理解它)。
我的应用创建数据可视化(使用 D3js,但这不是主题),并按以下方式组织我的代码:
function SVGBuilder( dataset ) {
this.data = dataset;
this.xCoord;
this.startDisplaying = function() {
// stuff
this.displayElement();
}
}
displayElement()
方法在继承自SVGBuilder(或多或少是一个抽象类)的"类"中定义。然后我有:
function SpiralBuilder( dataset ) {
SVGBuilder.call( this, dataset );
this.displayElement = function() {
// stuff
};
}
SpiralBuilder.inheritsFrom( SVGBuilder );
我还有其他几个基于相同结构的"构建器"。
调用构建器的脚本如下所示(它有点复杂,因为它根据用户输入选择正确的构造函数):
var builder = new SpiralBuilder( data );
builder.startDisplaying();
现在来了"转换部分"。我读了很多关于这方面的文章,从 Douglas Crockford 的文章,到 Eloquent Javascript 的部分内容。在Aadit M Shah的评论中,他提出了一个看起来像这样的结构:
var svgBuilder = {
data: [],
xCoord: 0, // ?
create: function( dataset ) {
var svgBuilder= Object.create(this);
svgBuilder.data = dataset;
return svgBuilder;
},
startDisplaying: function() {
// stuff
}
}
然而,在这一点上,我被困住了。首先(技术问题),我可以声明变量(数据,xCoord)而不在此模式中初始化它们吗?就像this.data;
一样?其次,我应该如何创建遗产?我只是手动将相应的功能放在原型中?像这样:
var spiralBuilder = builder.create( dataset );
spiralBuilder.prototype.displayElements = function() {
// Code to display the elements of a spiral
};
spiralBuilder.displayElements();
如果我是对的,这意味着在调用构建器的脚本中,我将不得不在单个构建器实例的原型中添加/修改方法,而不是选择正确的构造函数(这将不再存在)。事情应该这样做吗?
还是我应该尝试以完全不同的方式设计我的代码?如果是这样的话,你能给我一些建议/参考吗?
我可以声明变量(数据,xCoord)而不在此模式中初始化它们吗?
var svgBuilder = {
//removed data here as it's only going to shadowed
// on creation, defaults on prototype can be done
// if value is immutable and it's usually not shadowed later
create: function( dataset, xcoord ) {
var svgBuilder= Object.create(this);
svgBuilder.data = dataset;//instance variable
svgBuilder.xcoord = xcoord;//instance variable
return svgBuilder;
},
startDisplaying: function() {
// stuff
},
constructor : svgBuilder.create
};
我知道我很少在我的示例中这样做,但在创建实例或调用函数时,通常最好传递参数对象。
在某些时间点,您可能会在这里或那里更改内容,并且您不想更改代码中的许多位置。
在前几个示例中,您根本没有使用原型。每个成员都声明为构造函数中的this.something
,因此特定于实例的成员也是如此。
可以使用构建器,但是当您习惯于声明构造函数,原型,mixins和静态时,您所需要的只是一个用于继承和mixin的辅助函数。
可以在此处找到原型的介绍。它还进入继承、混合、覆盖、调用 super 和 this
变量。引言副本如下:
构造函数介绍
可以使用函数作为构造函数来创建对象,如果构造函数名为 Person,则使用该构造函数创建的对象是 Person 的实例。
var Person = function(name){
this.name = name;
};
Person.prototype.walk=function(){
this.step().step().step();
};
var bob = new Person("Bob");
Person 是构造函数,因为它是一个对象(就像 JavaScript 中的大多数其他任何东西一样),你也可以给它属性,比如: Person.static="something"
这适用于与 Person 相关的静态成员,例如:
Person.HOMETOWN=22;
var ben = new Person("Ben");
ben.set(Person.HOMETOWN,"NY");
ben.get(Person.HOMETOWN);//generic get function what do you thing it'll get for ben?
ben.get(22);//maybe gets the same thing but difficult to guess
当您使用 Person 创建实例时,您必须使用 new 关键字:
var bob = new Person("Bob");console.log(bob.name);//=Bob
var ben = new Person("Ben");console.log(bob.name);//=Ben
属性/成员name
是特定于实例的,对于 bob 和 ben 是不同的
所有实例共享成员walk
Bob 和 ben 是 Person 的实例,因此它们共享步行成员 (bob.walk===ben.walk)。
bob.walk();ben.walk();
因为 walk() 在 bob 上找不到,JavaScript 会在 Person.prototype 中查找它,因为这是 bob 的构造函数。如果在那里找不到,它将在 Function.prototype 上查找,因为 Person 的构造函数是 Function。函数的构造函数是 Object,所以它最后看到的是 Object.prototype。这称为原型链。
即使 bob、ben 和所有其他创建的 Person 实例共享 walk,该函数在每个实例的行为也会有所不同,因为在 walk 函数中它使用 this
.this
的值将是调用对象;现在,假设它是当前实例,因此对于bob.walk()
"this"将是Bob。(稍后将详细介绍"this"和调用对象)。
如果 ben 正在等待红灯,而 bob 处于绿灯状态;那么你将在 Ben 和 Bob 上调用 walk(),显然 Ben 和 Bob 会发生一些不同的事情。
当我们做类似 ben.walk=22
的事情时,也会发生影子成员,即使 bob 和 ben 共享walk
将 22 分配给 ben.walk 不会影响 bob.walk。这是因为该语句将直接在 ben 上创建一个名为 walk
的成员,并为其赋值 22。将有2个不同的步行成员:ben.walk和Person.prototype.walk。
当请求 bob.walk 时,你会得到 Person.prototype.walk 函数,因为在 bob 上找不到walk
。然而,请求 ben.walk 会得到值 22,因为成员步行是在 ben 上创建的,并且由于 JavaScript 在 ben 上找到了步行,它不会在 Person.prototype 中查找。
因此,成员的赋值将导致 JavaScript 无法在原型链中查找它并为其赋值。相反,它会将值分配给对象实例的现有成员,或者创建它,然后将值分配给它。
下一部分(有关原型的更多信息)将通过示例代码对此进行解释,并演示如何继承。
- JavaScript对象不是从原型链继承的
- 如何使用原型继承编写一个整洁灵活的复杂javascript应用程序
- $emit,$broadcast,原型继承
- Javascript:继承原型而不重新定义构造函数
- 原型继承未按预期工作
- JavaScript中的原型继承.我可以称之为“超级”等价物吗?
- 为什么函数对象的实例没有继承函数原型属性
- 不创建父对象的原型继承
- Javascript基本继承与Crockford原型继承
- JavaScript-构造函数参数和原型继承
- 使用Object.create作为原型的原型继承将[Object]作为实例的原型
- javascript继承中正确的原型做作是什么
- 如何进行JavaScript原型继承(原型链)
- Javascript原型继承原型函数调用
- 对象不继承原型函数
- 从其他类继承原型方法,而不重写自己的原型方法
- JavaScript继承原型
- 继承原型
- Javascript继承/原型混淆
- 使用.call(this)继承原型