对不起,我真的不知道如何更详细地说明问题。在这个例子中可以看到最常见的继承方式:
function Shape() {
this.x = 0;
this.y = 0;
}
//superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info("Shape moved.");
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); //call super constructor.
}
//subclass extends superclass
Rectangle.prototype = new Shape();
var rect = new Rectangle();
rect.move(2, 1);
我担心以下代码:
//subclass extends superclass
Rectangle.prototype = new Shape();
直接连接到父原型属性是不是更好。
Rectangle.prototype = Shape.prototype;
据我所知,直接从Object继承的所有对象都是这种情况。他们的原型不是连接到Object的某个实例,而是连接到它的原型属性?
在上面的例子中Shape.call(this);将复制属性(或类似的东西),我们只需要继承Shape.prototype中的那些方法。或者可能是我遗失了什么?
答案 0 :(得分:4)
我担心以下代码:
//subclass extends superclass Rectangle.prototype = new Shape();
你应该是,这是一个常见的模式,但不是一个非常好的模式,因为那时你使用Shape
两个创建原型和到初始化实例。它应该做一个或另一个。 (例如,考虑一下,如果你需要提供Shape
个参数,你会做什么?你会用什么args创建Rectangle.prototype
?)
但你也不想要Rectangle.prototype = Shape.prototype
,因为那时你无法向Rectangle.prototype
添加内容(因为它指向同一个Shape.prototype
指向的对象,你会将它们添加到Shape
个实例中。
所以我们想要的是创建一个新对象,使用Shape.prototype
作为其原型(例如,继承自Shape.prototype
),就像通过new Shape
创建的对象一样,但< em>没有调用Shape
来创建它;然后将该新对象用作Rectangle.prototype
。
从ECMAScript5开始,我们可以使用Object.create
:
Rectangle.prototype = Object.create(Shape.prototype);
Object.create
创建一个新对象,并将您提供的第一个参数指定为对象的基础原型。
完成后,虽然它不是严格必要,但我们还应该在新对象上设置constructor
属性,使其引用Rectangle
:
Rectangle.prototype.constructor = Rectangle;
我们这样做是因为这样,Rectangle.prototype
和Rectangle
之间的关系看起来就像§13.2 of the spec所说的那样。基本上,如果你有一个构造函数X
,那么X.prototype.constructor
应该引用X
。有时人们依赖于克隆操作等(JavaScript本身不依赖于此 - 例如,instanceof
不依赖它)。
因此我们可以使用ES5 Object.create
来执行此操作。但即使在今天,并非所有JavaScript引擎都支持Object.create
。因此,我们可以通过创建临时构造函数,让它借用Shape.prototype
作为其prototype
属性,然后使用该临时构造函数创建我们用作{{1 }}:
Rectangle.prototype
...我们应该再次设置function Ctor() { }
Ctor.prototype = Shape.prototype; // Borrow the prototype
Rectangle.prototype = new Ctor(); // Create the new object
属性:
constructor
通常,您不是每次都写出所有内容,而是使用这样的帮助:
Rectangle.prototype.constructor = Rectangle;
然后像这样使用它:
function derive(Parent, Child) {
function Ctor() { this.constructor = Child; }
Ctor.prototype = Parent.prototype;
Child.prototype = new Ctor();
return Child; // For chaining
}
所有这一切的最终结果是我们只从derive(Shape, Rectangle);
内调用Shape
来初始化实例。我们不会将其称为创建Rectangle
。
如果您对JavaScript中的继承管道感兴趣,您可能会对我的Lineage
script感兴趣,它会处理上述内容以及更多内容。当我说“感兴趣”时,我并不一定意味着使用它(虽然欢迎你),但是看看未经证实的来源,并this page比较Rectangle.prototype
做同样的事情,而不是帮助者可以让你知道这些东西是如何工作的。
答案 1 :(得分:1)
JavaScript是一种原型的面向对象编程语言。这就是说对象继承自其他对象。
JavaScript中的对象具有特殊的[[proto]]
属性,该属性指向该对象的原型。此属性由JavaScript引擎在内部维护,每个对象都有一个链接的原型链。例如:
null
^
| [[proto]]
|
+------------------+
| Object.prototype |
+------------------+
^
| [[proto]]
|
+------------------+
| Shape.prototype |
+------------------+
^
| [[proto]]
|
+---------------------+
| Rectangle.prototype |
+---------------------+
^
| [[proto]]
|
+------+
| rect |
+------+
当您编写Rectangle.prototype = new Shape();
时,您的原型链如上所示。请注意,执行此操作的新方法是Rectangle.prototype = Object.create(Shape.prototype);
。
null
^
| [[proto]]
|
+------------------+
| Object.prototype |
+------------------+
^
| [[proto]]
|
+---------------------+
| Rectangle.prototype |
| / Shape.prototype |
+---------------------+
^
| [[proto]]
|
+------+
| rect |
+------+
如果你写Rectangle.prototype = Shape.prototype;
,你的原型链将如上所示。由于Rectangle.prototype
与Shape.prototype
相同,因此您只会从一个原型而不是两个原型继承。这意味着您无法将Rectangle
具体方法添加到Rectangle.prototype
。
您应该选择哪种方法?我更喜欢使用第一种方法,因为我们有两个独立的原型。您可以将Rectangle
具体方法添加到Rectangle.prototype
。此外,它实际上是扩展原型。在第二种情况下,根本没有继承。
这就是我现在经常编写代码的方式:
function defclass(type, body) {
var neo = function () {
var self = new constructor;
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
};
var constructor = function () {}; constructor.prototype = type;
var prototype = constructor.prototype = new constructor; prototype.new = neo;
return body.call(prototype, type), prototype;
}
使用defclass
,您现在可以按如下方式创建类:
var shape = defclass(null, function () {
this.constructor = function () {
this.x = 0;
this.y = 0;
};
this.move = function (x, y) {
this.x += x;
this.y += y;
console.info("Shape moved.");
};
});
var rectangle = defclass(shape, function (shape) {
this.constructor = function () {
shape.constructor.call(this);
};
});
最后,您可以按如下方式创建类的实例:
var rect = rectangle.new();
rect.move(2, 1);
自己查看演示:http://jsfiddle.net/VHfBd/1
链接: