JavaScipt的继承方式

272 阅读2分钟

参考资料:《JavaScript高级程序设计4》

原型链继承

function Parent() {
    this.list = [1,2,3]
}
function Child() {
}
Child.prototype = new Parent()
const child1 = new Child()
const child2 = new Child()

缺点:

  1. 引用类型的属性被所有实例共享,如下:
...
child1.list.push(4)
console.log(child1)
console.log(child2)

image.png 2. 在创建 Child 的实例时,不能向Parent传参

借用构造函数继承(经典继承)

function Parent(name) {
    this.list = [1,2,3]
    this.name = name
}
function Child(name) {
    Parent.call(this,name)
}
const child1 = new Child('xxx')
const child2 = new Child('ssss')
child1.list.push(4)

优点:

  1. 避免了引用类型的属性被所有实例共享
  2. 可以在Child中向Parent传参 缺点: 方法都在构造函数中定义,每次创建实例都会创建一遍方法

组合继承

function Parent(name) {
    this.name = name
    this.list = [1, 2, 3]
}
Parent.prototype.sayName = function() {
    console.log(this.name)
}
function Child(name,age) {
    Parent.call(this, name)
    this.age = age
}

Child.prototype = new Parent()       //此时 Child.prototype 中的 constructor 被重写了,会导致 child1.constructor === Person
Child.prototype.constructor = Child  //将 Child 原型对象的 constructor 指针重新指向 Parent 本身
let child1 = new Child('xxx', '18');

child1.list.push(4);

console.log(child1.name);
console.log(child1.age);
console.log(child1.list); 

let child2 = new Child('sss', '20');

console.log(child2.name);
console.log(child2.age); 
console.log(child2.list); 

优点:融合原型链继承和构造函数的优点,是 JavaScript中最常用的继承模式。

原型式继承

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

这种方式也是ES5的Object.create的模拟实现方式

缺点:

  1. 与原型链继承一样引用类型的属性被所有实例共享
const obj = {
    name: 'xxx',
    list: [1,2,3]
}
const o1 = CreateObj(obj)
const o2 = CreateObj(obj)
o1.list.push(4)
console.log(o1)
console.log(o2)

image.png

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

function createObj (o) {
    var clone = Object.create(o);
    clone.sayName = function () {
        console.log('hi');
    }
    return clone;
}

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

寄生组合式继承

function Parent (name) {
    this.name = name;
    this.list = [1,2,3];
}

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

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

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();


var child1 = new Child('xxxx', 18);

console.log(child1);

最后我们封装一下这个继承方法:

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

function prototype(child, parent) {
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}
// 当我们使用的时候:
prototype(Child, Parent);

引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是:

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

写在最后

加油搬砖人~~