拥有Java背景,当我切换到Javascript时,我(懒惰地)试图坚持我对oop的了解,即经典的继承。我正在开发一个web-app(我制作),并使用了这种继承。但是,我正在考虑更改我的代码并重写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's article到Eloquent Javascript的部分内容阅读了很多相关内容。在Aadit M Shah的comment中,他提出了一个看起来像这样的结构:
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();
如果我是正确的,这意味着在调用构建器的脚本中,而不是选择正确的构造函数(不再存在),我将不得不在单个构建器的原型中添加/修改方法实例。是应该怎么做的?
或者我应该尝试以完全不同的方式设计我的代码?如果是这样的话,你可以给我一些建议/参考吗?
答案 0 :(得分:1)
我可以声明变量(data,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
,因此是特定于实例的成员。
可以使用构建器,但是当你很自然地声明构造函数,原型,混合ins和静态时,你只需要一个辅助函数来继承和混合。
可以找到原型简介here。它还包括继承,混合输入,覆盖,调用超级和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创建实例时,必须使用新关键字:
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的实例,因此他们共享walk成员(bob.walk === ben.walk)。
bob.walk();ben.walk();
因为无法在bob上找到walk(),所以JavaScript会在Person.prototype中查找它,因为这是bob的构造函数。如果在那里找不到它,它将在Function.prototype上查找,因为Person的构造函数是Function。函数的构造函数是Object,所以它看起来最后的东西是Object.prototype。这被称为原型链。
尽管bob,ben和所有其他创建的Person实例共享walk,但每个实例的函数行为都不同,因为在walk函数中它使用this
。 this
的值将是调用对象;现在让我们说它是当前的实例,所以bob.walk()
“这个”将是bob。 (更多关于“this”和稍后调用的对象)。
如果本正在等待红灯而且鲍勃处于绿灯状态;然后你会在ben和bob上调用walk(),显然ben和bob会发生不同的事情。
当我们执行类似ben.walk=22
之类的操作时会发生隐藏成员,即使bob和ben共享walk
22的赋值到ben.walk也不会影响bob.walk。这是因为该语句将直接在ben上创建一个名为walk
的成员,并为其赋值22.将有2个不同的walk成员:ben.walk和Person.prototype.walk。
当要求bob.walk时,你将获得Person.prototype.walk函数,因为在bob上找不到walk
。然后要求ben.walk将获得值22,因为成员行走已在ben上创建,因为JavaScript发现在Ben上行走它不会在Person.prototype中查找。
因此,成员的分配会导致JavaScript无法在原型链中查找并为其赋值。相反,它会将值分配给对象实例的现有成员或创建它,然后将值赋给它。
The next part(有关原型的更多信息)将使用示例代码解释这一点并演示如何继承。