前端面试系列-JavaScript-继承的八种实现方案以及优缺点

我心飞翔 分类:javascript

继承

继承的本质就是复制,即重写原型对象,代之以一个新类型的实例。

   function parentClass(name){
        this.name = name;
    }

    function sonClass(){
    }
    
 

1.原型链继承

sonClass.prototype = new parentClass();
 

缺点:

  1. 不能向构造函数传参
  2. 引用类型的属性被所有实例共享

2.构造函数式继承

使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)

 function sonClass(name){
     parentClass.call(this,name);
 }
 

缺点:

  1. 方法都在构造函数中定义,每次创建实例都会创建一遍,无法实现复用
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法

优点:
3. 避免了引用类型的属性被所有实例共享(创建子类实例时调用父类构造函数,每个实例都会将父类中的属性复制一份。)
4. 可以在 Child 中向 Parent 传参

例子:

function parentClass(age) {
    this.names = ['tom', 'jack'];
    this.age = age;
}

function sonClass(age) {
    parentClass.call(this, age);
}

var child1 = new sonClass(18);
child1.names.push('mike');

console.log(child1.names,child1.age);// ["tom", "jack", "mike"] 18

var child2 = new sonClass(20);

console.log(child2.names,child2.age); //["tom", "jack"] 20
 

3.组合继承(常用)

原型链继承和构造函数式继承组合。
用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。

function sonClass(name,age){
    parentClass.call(this,name); // 第二次调用parentClass()
    this.age = age;
}
sonClass.prototype = new parentClass();//第一次调用parentClass()
sonClass.prototype.constructor = sonClass;// 重写sonClass.prototype的constructor属性,指向自己的构造函数sonClass
 

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

缺点:父类的构造函数执行了两遍:一次在子类的构造函数中call方法执行一遍,一次在子类原型实例化父类的时候执行一遍。

4.原型式继承

就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。

function createObj(o){
    function F(){}
    F.prototype = o;
    return new  F();
}
var son = createObj(parent);
 

缺点:

  1. 和原型链继承一样,父类对象的引用类型值被共用。
  2. 无法传递参数

5.寄生式继承

在原型式继承的基础上,增强对象,返回构造函数
主要作用是为构造函数新增属性和方法,以增强函数

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

缺点(同原型式继承):

  1. 父类对象的引用类型值被共用。
  2. 无法传递参数

6.寄生组合式继承

结合借用构造函数传递参数和寄生模式实现继承

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

function inheritPrototype(sonClass,parentClass){
    var p = inheritObject(parentClass.prototype);// 创建对象,创建父类原型的一个副本
    p.constructor = sonClass; // 增强对象,弥补因重写原型而失去的默认的constructor 属性
    sonClass.prototype = p;// 指定对象,将新创建的对象赋值给子类的原型
}

// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function sonClass(name,age){
    parentClass.call(this,name);
    this.age = age;
}

inheritPrototype(sonClass,parentClass);
 

这是最成熟的方法,也是现在库实现的方法。

7.Object.assign

Object.assign会把 Parent原型上的函数拷贝到 Child原型上,使 Child的所有实例都可用 Parent的方法。它将返回目标对象,如果有重复的属性会被覆盖。

Object.assign(Child.prototype,Parent.prototype);
Child.prototype.constructor = Child;
 

8. ES6类Extends

使用extends表明继承自哪个父类,并且在子类构造函数中必须使用super,可以看作是 Parent.call(this,value);

class Child extends parentClass{
    constructor(value){
        super(value);
        this.val = value;
    }
}
 

回复

我来回复
  • 暂无回复内容