回到课程

类扩展自对象?

重要程度: 3

正如我们所知道的,所有的对象通常都继承自 Object.prototype,并且可以访问“通用”对象方法,例如 hasOwnProperty 等。

例如:

class Rabbit {
  constructor(name) {
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

// hasOwnProperty 方法来自于 Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true

但是,如果我们像这样 "class Rabbit extends Object" 把它明确地写出来,那么结果会与简单的 "class Rabbit" 有所不同么?

不同之处在哪里?

下面是此类的示例代码(它无法正常运行 — 为什么?修复它?):

class Rabbit extends Object {
  constructor(name) {
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

alert( rabbit.hasOwnProperty('name') ); // Error

首先,让我们看看为什么之前的代码无法运行。

如果我们尝试运行它,就会发现原因其实很明显。派生类的 constructor 必须调用 super()。否则 "this" 不会被定义。

下面是修复后的代码:

class Rabbit extends Object {
  constructor(name) {
    super(); // 需要在继承时调用父类的 constructor
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

alert( rabbit.hasOwnProperty('name') ); // true

但这还不是全部原因。

即便修复了它,"class Rabbit extends Object"class Rabbit 之间仍存在着重要差异。

我们知道,“extends” 语法会设置两个原型:

  1. 在构造函数的 "prototype" 之间设置原型(为了获取实例方法)。
  2. 在构造函数之间会设置原型(为了获取静态方法)。

在我们的例子里,对于 class Rabbit extends Object,它意味着:

class Rabbit extends Object {}

alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) true

所以,现在 Rabbit 可以通过 Rabbit 访问 Object 的静态方法,像这样:

class Rabbit extends Object {}

// 通常我们调用 Object.getOwnPropertyNames
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b

但是如果我们没有 extends Object,那么 Rabbit.__proto__ 将不会被设置为 Object

下面是示例:

class Rabbit {}

alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) false (!)
alert( Rabbit.__proto__ === Function.prototype ); // true,所有函数都是默认如此

// error,Rabbit 中没有这样的函数
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error

所以,在这种情况下,Rabbit 没有提供对 Object 的静态方法的访问。

顺便说一下,Function.prototype 有一些“通用”函数方法,例如 callbind 等。在上述的两种情况下它们都是可用的,因为对于内建的 Object 构造函数而言,Object.__proto__ === Function.prototype

我们用一张图来解释:

所以,简而言之,这里有两点区别:

class Rabbit class Rabbit extends Object
needs to call super() in constructor
Rabbit.__proto__ === Function.prototype Rabbit.__proto__ === Object