继承链和构造函数
如之前所见,JavaScript 对象拥有一个 prototype
属性,它被设计用来实现继承。一个对象的 prototype
属性可以被设置为另一个对象的实例,以此创建一个继承链:
function Shape(name) {
this.x = 0;
this.y = 0;
this.name = name;
console.log('Shape constructor called');
}
Shape.prototype = {
move: function(x, y) {
this.x += x;
this.y += y;
},
toString: function() {
return 'name: ' + this.name + ', at x: ' + this.x + ', y:' + this.y;
}
};
// Rectangle
function Rectangle(name) {
this.name = name;
console.log('Rectangle constructor called');
}
Rectangle.prototype = new Shape();
var rect = new Rectangle('Player 1');
rect.move(1, 1);
console.log(rect.toString());
console.log(rect instanceof Rectangle);
运行会显示下面的输出:
Shape constructor called
Rectangle constructor called
name: Player 1, at x: 1, y:1
true
注意无论 Shape
还是 Rectangle
构造函数都被调用了。这是因为这一行 Rectangle.prototype = new Shape();
。
另外需要注意到 rect.move
和 rect.toString
会从 Shape.prototype
中调用方法。当解释器检查一个属性,首先会在当前对象中查找。如果没有找到属性,则会在对象的原型中查找,并以此类推。这就是原型链:
首先在被涉及的对象中直接查找属性,如果对象包含该命名属性,则引用此命名属性;如果对象不包含该命名属性,对象的原型会被检查;以此类推。
调用父级方法
如果 Rectangle
想要一个不同的 move
方法,但想重用 Shape
中初始的方法,那么完全可以使用 Function.prototype.apply
来实现:
Rectangle.prototype.move = function(x, y) {
console.log('Super method called');
Shape.prototype.move.apply(this, arguments);
};
尽管 Shape.prototype.move.apply
看起来很复杂,但如果我们把它分解,实际上很简单:
- 我们想要调用来自
Shape
的move
方法 - 此方法保存在
Shape.prototype.move
- 因为它是一个
Function
,所以有一些我们可用的方法(函数是对象!) - apply 特别的允许我们调用函数,而无需创建一个新实例
- 它还允许我们提供一个
this
值,以及一个参数数组
当一个函数执行时,参数对象由解释器创建。this
对象是另外一个故事了 -- 到目前为止我假设你已经直观的理解它是什么了,但我们会在接下来的部分更详细的看看它。
引用