JS组合继承相比原型链继承的优势

原型继承:

// '父'构造函数

function Father() {

    this.name = 'foo';

    this.family = ['Jack','Tom','James'];

}

Father.prototype.sayName = function () {

    console.log(this.name);

};

Father.prototype.sayFamily = function () {

};

// '子'构造函数

function Child() {

}

// 实现继承

Child.prototype = new Father();

// 看看继承之后的效果

let childInstance1 = new Child();

childInstance1.sayName(); // 'foo'

childInstance1.sayFamily(); // ['Jack','Tom','James']

通过将子构造函数的原型设置为父构造函数实例的方法实现了继承,这就是原型链继承。在这个例子中,子构造函数的实例本身是没有 name 和 family 属性的,这两个属性都是通过原型链在子构造函数的原型上找到的,而子构造函数的原型来自于父构造函数的一个实例。 子构造函数的实例本身也是没有 sayName 和 sayFamily 方法的,沿着原型链查找,子构造函数的原型中也没有这两个方法,再向上查找,这两个方法来自与父构造函数的原型上。

原型链继承的问题:

通过这种继承方法,有一个比较明显的问题,我们知道,引用类型的原型属性会被所有的实例共享,那么在上面的例子中,由于子构造函数的原型是父构造函数的实例,所以 family 属性(数组是引用类型)会被子构造函数的所有实例共享。也就是说,当我们修改了子构造函数一个实例上的 family 属性时,会对子构造函数原型上的该属性直接产生影响,从而影响到子构造函数的全部实例。

// 修改childInstance2的属性值,看看会有什么影响

let childInstance2 = new Child();

childInstance2.name = 'bar';

childInstance2.family.push('curry');

childInstance1.sayName(); // 'foo'

childInstance1.sayFamily(); // ['Jack','Tom','James','curry']

childInstance2.sayName(); // 'bar'

childInstance2.sayFamily(); // ['Jack','Tom','James','curry']

我们看到,修改了 childInstance2 的 name 属性(字符串是基础类型),不会对其他实例产生影响,但是当我们修改了 family 属性时,就会造成 childInstance1 的 family 属性也被修改了。这显然会带来很多的麻烦。

组合继承:思路是通过原型链实现对原型属性和方法的继承,但是利用构造函数实现对实例属性的继承,避免属性被共享。

// '父'构造函数

function Father() {

this.family = ['Jack','Tom','James'];

}

Father.prototype.sayFamily = function () {

console.log(this.family);

};

// '子'构造函数

// 这里利用构造函数把父构造函数的属性直接放到子构造函数属性中

function Child() {

Father.call(this);

}

// 实现继承

Child.prototype = new Father();

// 看看继承之后的效果

let childInstance1 = new Child();

childInstance1.sayFamily(); // ['Jack','Tom','James']

// 修改subInstance2的属性值,看看会有什么影响

let childInstance2 = new Child();

childInstance2.family.push('curry');

childInstance1.sayFamily(); // ['Jack','Tom','James']

childInstance2.sayFamily(); // ['Jack','Tom','James','curry']

采用组合继承,子构造函数创造出来的实例本身就有 family 属性(构造函数具有的属性和方法都在实例本身),不必再通过原型链向原型查找,也就避免了属性被共享的问题。

猜你喜欢

转载自blog.csdn.net/RUCwang/article/details/81479110