RN-《JavaScript高级程序设计》第六章继承

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Parent() {
this.colors = ['red','blue','yellow'];
}

function Child() {}

Child.prototype = new Parent(); // 核心
Child.prototype.constructor = Child;

var child1 = new Child();

child1.colors.push('orange');

console.log(child1.colors); //  ["red", "blue", "yellow", "orange"]

var child2 = new Child();

console.log(child2.colors); // ["red", "blue", "yellow", "orange"]

实现的本质是重写原型对象,代之以一个新类型的实例。

问题:

  1. 引用类型的属性被所有实例共享
  2. 在创建 Child 的实例时,不能向Parent传参

借用构造函数

这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。别忘了,函数只不过是在特定环境中执行代码的对象,因此通过使用 apply()和 call()方法也可以在(将来)新创建的对象上执行构造函数。

可以在子类型构造函数中向超类型构造函数传递参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent(name) {
this.name = name;
this.colors = ['red','blue','yellow'];
}

function Child(name) {
Parent.call(this,name); // 核心
}

let child1 = new Child("gzl1");

child1.colors.push('orange');

console.log(child1.colors); //  ["red", "blue", "yellow", "orange"]
console.log(child1.name); // gzl1

let child2 = new Child("gzl2");

console.log(child2.colors); // ["red", "blue", "yellow"]
console.log(child2.name); // gzl2

问题:方法都在构造函数中定义

组合继承

组合继承(combination inheritance),有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

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
function Parent(name) {
this.name = name;
this.colors = ['red','blue','yellow'];
}

function Child(name,age) {
Parent.call(this,name); // 核心
this.age = age;
}

Child.prototype = new Parent(); // 核心
Child.prototype.constructor = Child;

let child1 = new Child("gzl1",18);

child1.colors.push('orange');

console.log(child1.colors);//  ["red", "blue", "yellow", "orange"]
console.log(child1.name); // gzl1
console.log(child1.age); // 18

let child2 = new Child("gzl2",19);

console.log(child2.colors);// ["red", "blue", "yellow"]
console.log(child2.name); // gzl2
console.log(child2.age); // 19

问题:会调用两次父构造函数,且child1,2的原型中会有colors

组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。而且,instanceof 和 isPrototypeOf()也能够用于识别基于组合继承创建的对象。

原型式继承

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
function createObj(o) {
function F(){}
F.prototype = o;
return new F();
}
//ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型
var person = {
name: 'gzl',
friends: ['x', 'y']
}

var person1 = createObj(person);
person1.name = 'gzl1'; // 并非修改了原型上的 name 值
person1.friends.push('z');

var person2 = createObj(person);
person2.name = 'gzl2'; // 并非修改了原型上的 name 值
person2.friends.push('c');


console.log(person1.name); // gzl1
console.log(person1.friends); // ["x", "y", "z", "c"]

console.log(person2.name); // gzl2
console.log(person2.friends); // ["x", "y", "z", "c"]

问题:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样

寄生式继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createObj (o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log(this.friends);
}
return clone;
}

var person = {
name: 'gzl',
friends: ['x', 'y']
}

var person1 = createObj(person);
person1.sayName(); //  ["x", "y"]

问题:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

寄生组合式继承

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
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
console.log(this.name)
}

function Child (name, age) {
Parent.call(this, name);
this.age = age;
}

inheritPrototype(Child,Parent);

function obj(o) {
function F() {}
F.prototype = o;
return new F();
}

function inheritPrototype(child, parent) {
var prototype = obj(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}

var child1 = new Child("gzl1",18);
child1.colors.push('orange');
console.log(child1.colors); // ["red", "blue", "green", "orange"]

var child2 = new Child("gzl2",19);
console.log(child2.colors); // ["red", "blue", "green"]

开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。