JavaScript语言这独特的继承方式你都了解吗?(六种)

937 阅读3分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第5篇文章,点击查看活动详情

前言

众所周知在JavaScript也有类的概念,但是JavaScript中的类不同于其他语言(java、c++等)的类。在那些面向类的语言中,类继承的本质是复制;而在JavaScript中严格来说并没有真正的类,它的类继承的方式比较特别,是通过原型链接来继承的,也就是说在实现类继承之后,再对父类的原型对象上的方法进行修改,子类也同样会受到影响。

好了,了解到了JavaScript不同于其他面向类的语言的独特的继承方式了,现在来聊聊JavaScript如何实现继承。这也是面试常考的一个重点,大家务必全部阅读完。

继承方式

1.原型链继承

function Cat(){ }
function Animal(){}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

介绍: 在这里我们可以看到 new 了一个空对象,这个空对象指向 Animal 并且 Cat.prototype 指向了这个空对象,这种就是基于原型链的继承。

特点: 基于原型链,既是父类的实例,也是子类的实例

缺点: 无法实现多继承

2.构造继承

function Animal(){}
function Cat(name){
    Animal.call(this);
    this.name = name || 'Tom';
    }
    // Test Code
    var cat = new Cat();
    console.log(cat.name);//Tom
    console.log(cat instanceof Animal); // false
    console.log(cat instanceof Cat); // true
    

介绍: 使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给 子类(没用到原型)

特点: 可以实现多继承

缺点: 只能继承父类实例的属性和方法,不能继承原型上的属性和方法。

3.组合继承

function Animal(){}
function Cat(name){
    Animal.call(this);
    this.name = name || 'Tom';
    }
    Cat.prototype = new Animal();
    Cat.prototype.constructor = Cat;
    // Test Code
    var cat = new Cat();
    console.log(cat.name);//Tom
    console.log(cat instanceof Animal); // true
    console.log(cat instanceof Cat); // true

介绍: 相当于构造继承和原型链继承的组合体。通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

特点: 可以继承实例属性/方法,也可以继承原型属性/方法

缺点: 调用了两次父类构造函数,生成了两份实例

4.寄生组合继承

function Animal(){}
function Cat(name){
    Animal.call(this);
    this.name = name || 'Tom';
    }
    (function(){
    // 创建一个没有实例方法的类
    var Super = function(){};
    Super.prototype = Animal.prototype;
    //将实例作为子类的原型
    Cat.prototype = new Super();
    })();
    // Test Code
    var cat = new Cat();
    console.log(cat.name);//Tom
    console.log(cat instanceof Animal); // true
    console.log(cat instanceof Cat); //true

介绍: 通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的 构造的时候,就不会初始化两次实例方法/属性,解决了组合继承的缺陷

优点: 寄生组合式继承的高效率体现在它只调用了一次父类型构造函数,避免了创建不必要的或多余的属性,与此同时,原型链还能保持不变。

推荐: 推荐使用该方式来实现继承,这也是目前来说最理想的继承方式了

还有两种是在其他博主那学习过来的,现在适用性不强,主要推荐使用寄生组合继承,大家可以了解一下:

5.原型式继承

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

var person = {
    name : 'wuyuchang',
    friends : ['wyc', 'Nicholas', 'Tim']
}

var anotherPerson = Animal(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Bob');

var anotherPerson2 = Animal(person);
anotherPerson2.name = 'Jack';
anotherPerson2.friends.push('Rose');

console.log(person.friends);  // [ 'wyc', 'Nicholas', 'Tim', 'Bob', 'Rose' ]

介绍: 原型式继承的的实现方法与普通继承的实现方法不同,原型式继承并没有使用严格意义上的构造函数,而是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

6.寄生式继承

/* 原型式继承 */
function Animal(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

/* 寄生式继承 */
function createAnother(original) {
    var clone = Animal(original);
    clone.sayHi = function() {
        console.log('hi');
    }
    return clone;
}

var person = {
    name : 'wuyuchang',
    friends : ['wyc', 'Nicholas', 'Rose']
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();//hi