# ❓ES5/ES6 的继承除了写法以外还有什么区别?

# 继承差异:

  • ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到 this 上面(所以必须先调用 super 方法),然后再用子类的构造函数修改 this。

  • ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的 prototype 属性。 Class 作为构造函数的语法糖,同时有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链。

    1. 子类的 __proto__ 属性,表示构造函数的继承,总是指向父类。
    2. 子类 prototype 属性的 __proto__ 属性,表示方法的继承,总是指向父类的 prototype 属性。
    class A {}
    
    class B extends A {}
    
    B.__proto__ === A; // true
    B.prototype.__proto__ === A.prototype; // true
    
    1
    2
    3
    4
    5
    6

    为什么是这样实现的

除继承外的一些差异,我们的 🌰

// es5
function AnimalEs5(name) {
  this.name = name;
}

AnimalEs5.prototype.getName = function() {
  return this.name;
};

// es6
class AnimalEs6 {
  constructor(name) {
    this.name = name;
  }

  static getType() {
    return "AnimalEs6";
  }

  getName() {
    return this.name;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • class 声明不会变量提升
new Foo(); // ReferenceError: Foo is not defined

class Foo {
1
2
3

上面代码中,Foo 类使用在前,定义在后,这样会报错,因为 ES6 不会把类的声明提升到代码头部。这种规定的原因也与继承有关,必须保证子类在父类之后定义。

  • class 必须使用 new 调用,否则会报错
// es5 调用
AnimalEs5(); // it's ok

// es6 调用
AnimalEs6(); // Class constructor AnimalEs6 cannot be invoked without 'new'
1
2
3
4
5
  • class 的内部,默认就是严格模式

类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

// 引用一个未声明的变量
function Bar() {
  baz = 42; // it's ok
}
const bar = new Bar();

class Foo {
  constructor() {
    fol = 42; // ReferenceError: fol is not defined
  }
}
const foo = new Foo();
1
2
3
4
5
6
7
8
9
10
11
12
  • class 内部所有定义的方法都是不可枚举的(non-enumerable)
// es5 调用
Object.keys(AnimalEs5.prototype); // [ 'getName' ]
Object.getOwnPropertyNames(AnimalEs5.prototype); // [ 'constructor', 'getName' ]

// es6调用
Object.keys(AnimalEs6.prototype); // []
Object.getOwnPropertyNames(AnimalEs6.prototype); // [ 'constructor', 'getName' ]
1
2
3
4
5
6
7
  • class 内声明的所有的方法(包括静态方法和实例方法)都没有原型对象 prototype 所以也没有 [[construct]],不能用 new 来调用
const dog = new AnimalEs5();
const dogName = new dog.getName(); // it's ok

const es6Dog = new AnimalEs6();
const es6DogName = new es6Dog.getName(); // TypeError: es6Dog.getName is not a constructor
1
2
3
4
5
  • class 内部类名无法被重写
class AnimalEs6 {
  constructor(name) {
    this.name = name;
    // AnimalEs6 = "AnimalEs6Change"; // TypeError: Assignment to constant variable.
    // asd = "dad"; // ReferenceError: asd is not defined 严格模式
  }

  static getType() {
    return "AnimalEs6";
  }

  getName() {
    return this.name;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如果大家有看 test🌰 的注意报错哦。