javascript 对象继承 - 子类和超类
javascript object inheritance - subclass and superclass
这两种方法有什么区别?
第一种方法
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this);
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
第二种方法
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this);
}
// subclass extends superclass
Rectangle.prototype = new Shape();
var rect = new Rectangle();
最大的实际区别是,当您使用 new 时,将调用超级 class 的构造函数,而不需要 Shape.call(this);
。
主要区别在于,在第二种方法中,Shape
构造函数仅被调用一次。在第一种方法中,不调用 Shape
构造函数,而是 Object.create(Shape.prototype)
从原型创建一个新对象。然后可以使用 Shape.call
.
为每个子构造调用一次父构造函数
您仍然可以像第二种方法一样为每个子类调用 Shape.call
,但它会导致在初始原型创建期间额外调用构造函数。
此外,使用第一种方法 Object.create
会导致 constructor
属性 的 prototype
被覆盖,因此 Rectangle.prototype.constructor = Rectangle;
用于重置constructor
属性.
第二种方法的主要缺点是它需要额外调用一次 Shape
构造函数才能创建一个对象用作 Rectangle
的原型。有不同的方法来避免它,例如使用中间空函数:
function Rectangle() {
Shape.call(this);
}
function f() {}
f.prototype = Shape.prototype;
Rectangle.prototype = new f();
如您所见,这显然很笨拙。
出于这个原因,使用 Object.create
的模式 #1 更方便、更高效。
但是请注意,这两个片段都有另一个问题:自己的属性是用 Shape.call(this);
复制的,这又是不受欢迎的。
Object.create 不执行构造函数。
用 New 调用相当于 Object.create(object.prototype) + 运行 构造函数。
我看到了两个区别:一大一小。
细微差别在于,在第二种方法中,Rectangle
原型不会正确设置 constructor
属性。它将指向 Shape
,而不是 Rectangle
。这是 JavaScript OO 方法中的一个极其常见的错误,大多数时候你可以摆脱它,因为人们不经常使用 constructor
属性,但它就在那里.
最大的不同是在第二个构造函数中额外调用了Shape()
。在您的特定示例中,事情或多或少地解决了问题:Rectangle.prototype
有一些您可能不想要的额外属性(x
和 y
,两者都为零)有,但无论如何这些都会被 Rectangle
实例中的相应属性所掩盖。同样,这种非常常见但很小的错误经常发生,您可以摆脱它。
第二种方法的问题在于,它仅在超类构造函数不需要任何参数时才有效。您的示例恰好符合该描述,因为它所做的只是将某些属性初始化为默认值。但是存在这种设置不太合适的用例,在这些情况下不能使用第二种方法。
这两种方法有什么区别?
第一种方法
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this);
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
第二种方法
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this);
}
// subclass extends superclass
Rectangle.prototype = new Shape();
var rect = new Rectangle();
最大的实际区别是,当您使用 new 时,将调用超级 class 的构造函数,而不需要 Shape.call(this);
。
主要区别在于,在第二种方法中,Shape
构造函数仅被调用一次。在第一种方法中,不调用 Shape
构造函数,而是 Object.create(Shape.prototype)
从原型创建一个新对象。然后可以使用 Shape.call
.
您仍然可以像第二种方法一样为每个子类调用 Shape.call
,但它会导致在初始原型创建期间额外调用构造函数。
此外,使用第一种方法 Object.create
会导致 constructor
属性 的 prototype
被覆盖,因此 Rectangle.prototype.constructor = Rectangle;
用于重置constructor
属性.
第二种方法的主要缺点是它需要额外调用一次 Shape
构造函数才能创建一个对象用作 Rectangle
的原型。有不同的方法来避免它,例如使用中间空函数:
function Rectangle() {
Shape.call(this);
}
function f() {}
f.prototype = Shape.prototype;
Rectangle.prototype = new f();
如您所见,这显然很笨拙。
出于这个原因,使用 Object.create
的模式 #1 更方便、更高效。
但是请注意,这两个片段都有另一个问题:自己的属性是用 Shape.call(this);
复制的,这又是不受欢迎的。
Object.create 不执行构造函数。
用 New 调用相当于 Object.create(object.prototype) + 运行 构造函数。
我看到了两个区别:一大一小。
细微差别在于,在第二种方法中,Rectangle
原型不会正确设置 constructor
属性。它将指向 Shape
,而不是 Rectangle
。这是 JavaScript OO 方法中的一个极其常见的错误,大多数时候你可以摆脱它,因为人们不经常使用 constructor
属性,但它就在那里.
最大的不同是在第二个构造函数中额外调用了Shape()
。在您的特定示例中,事情或多或少地解决了问题:Rectangle.prototype
有一些您可能不想要的额外属性(x
和 y
,两者都为零)有,但无论如何这些都会被 Rectangle
实例中的相应属性所掩盖。同样,这种非常常见但很小的错误经常发生,您可以摆脱它。
第二种方法的问题在于,它仅在超类构造函数不需要任何参数时才有效。您的示例恰好符合该描述,因为它所做的只是将某些属性初始化为默认值。但是存在这种设置不太合适的用例,在这些情况下不能使用第二种方法。