继承链(原型链)以及构造函数

JavaScript 对象都有一个 prototype 属性,这个属性的作用就是为了方便的进行 继承。一个对象的原型属性,可以被设置为另外一个对象创建的继承链的实例,也就是说,对象 B在创建的时候,可以继承对象 A 的 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 这两个构造函数( constructor )都被调用了。这是因为这句 Rectangle.prototype = new Shape();–new Rectangle() 并不会使其父对象的构造函数自动被调用,这也就是为什么两个构造函数都有 this.name = name; 了。

这里的 Shape.prototype 的方法 rect.move 和 rect.toString,当代码执行到这里的时候,如果构造函数 Rectangle 中有此方法,则就执行此方法,如果构造函数 Rectangle 中 没有此方法,则会向上寻找到 Rectangle.prototype ,如果找到,则执行,否则则返回 undefined 。如果构造函数 Rectangle 中和 Rectangle.prototype 都有此方法的话,则优先使用构造函数 Rectangle 中的方法,而不是 Rectangle.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 看起来很复杂, 但是如果把它拆解开来看的话,其实也比较简单。

1. 想要使用 Shape 调用 move 方法。
2. 此方法被存储在 Shape.prototype.move 中。
3. 由于是一个函数,所以有很多方法可以调用。
4. apply 方法可以调用一个函数而不用创建一个新实例。
5. 可以使用一些参数。

当函数执行的时候,arguments 对象,会被解释器所创建,其实就是传入的参数列表,类似参数的数组。而 this 则又是两一个深奥的东西了,那么下次再说。