JavaScript对象的继承有哪几种

javascript 高级程序设计中有写,对象的继承总共有六种:

  1. 原型链继承,子原型是父原型的一个实例
  2. 借用构造函数继承,子构造函数调用父构造函数
  3. 组合式继承(组合 1 和 2)
  4. 原型式继承
  5. 寄生式继承
  6. 寄生式组合继承(组合 3 和 5)

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Parent() {
// A的非引用属性
this.a = 1;
// A的引用属性
this.array = [1, 2, 3];
}
// A的静态方法
Parent.prototype.func1 = () => {
console.log("func1");
};
function Child() {
this.b = 2;
}
Child.prototype = new Parent(); // 子原型是父原型的一个实例
Child.prototype.func2 = () => {
console.log("func2");
};
const child1 = new Child();
const child2 = new Child();
child1.a = 3;
child1.array.push(4);
console.log(child1.a, child2.array, child1.func1);

缺点是:由于子原型是父原型的一个实例,所以父级属性在子级的原型上,而不在每个子级的实例上,所以如果属性是引用类型时会互相影响。

借用构造函数继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Parent() {
// A的非引用属性
this.a = 1;
// A的引用属性
this.array = [1, 2, 3];
}
// A的静态方法
Parent.prototype.func1 = () => {
console.log("func1");
};
function Child() {
// 把父级属性绑定到子实例里面
Parent.call(this);
this.b = 2;
}
Child.prototype.func2 = () => {
console.log("func2");
};
const child1 = new Child();
const child2 = new Child();
child1.a = 3;
child1.array.push(4);
console.log(child1.a, child2.array, child1.func1);

解决了原型链继承中,属性没绑定到子实例里面的问题,但如果仅这样继承的话,父原型上的东西没有被继承

组合式继承

综合了以上两种继承方式,可以既继承原型,又把属性绑定到了子实例(从而覆盖原型的属性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Parent() {
// A的非引用属性
this.a = 1;
// A的引用属性
this.array = [1, 2, 3];
}
// A的静态方法
Parent.prototype.func1 = () => {
console.log("func1");
};
function Child() {
// 把父级属性绑定到子实例里面,此处第二次调用父构造函数
Parent.call(this);
this.b = 2;
}
Child.prototype = new Parent(); // 子原型是父原型的一个实例,此处第一次调用父构造函数
Child.prototype.func2 = () => {
console.log("func2");
};
const child1 = new Child();
const child2 = new Child();
child1.a = 3;
child1.array.push(4);
console.log(child1.a, child2.array, child1.func1);

缺点是:

  1. 父构造函数执行了两次
  2. 父实例上存储了多余的属性(压根用不到),浪费内存

原型式继承

创建一个临时的构造函数,并把这个临时的构造函数的 prototype 指向父原型

1
2
3
4
5
function object(Parent) {
function F() {}
F.prototype = Parent;
return new F();
}

相当于Object.create(),ECMAScript 5 通过增加 Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的 object()方法效果相同

寄生式继承

在原型式继承的基础上,做了一个封装:

1
2
3
4
5
6
7
function createAnother(Parent) {
const clone = object(Parent);
clone.sayHi = function () {
console.log("Hi");
};
return clone;
}

寄生式组合继承

综合了寄生式继承和组合式继承,并克服了组合式继承的缺点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function object(Parent) {
function F() {}
F.prototype = Parent;
return new F();
}
function inheritPrototype(Child, Parent) {
// 拷贝了一份父级的原型,而非生成父级的实例,所以上面没有父级的实例的属性
const p = object(Parent.prototype);
p.constructor = Child;
Child.prototype = p;
}
function Parent() {
// A的非引用属性
this.a = 1;
// A的引用属性
this.array = [1, 2, 3];
}
// A的静态方法
Parent.prototype.func1 = () => {
console.log("func1");
};
function Child() {
// 把父级属性绑定到子实例里面
Parent.call(this);
this.b = 2;
}
inheritPrototype(Child, Parent);
Child.prototype.func2 = () => {
console.log("func2");
};
const child1 = new Child();
const child2 = new Child();
child1.a = 3;
child1.array.push(4);
console.log(child1.a, child2.array, child1.func1);

ES6 的 class 语法糖,就是寄生式组合继承

参考