常见的实现继承的方式有以下几种:

1.原型链式继承

2.构造函数式继承

3.组合式继承

4.原型式继承

5.寄生式继承

6.寄生组合式继承

下面来详细讲些以下每种继承的实现方式,及优缺点

 

先声明一个叫Parent的构造函数  (引:JavaScript中的构造函数是一种特殊的函数,它用于创建新的对象实例)

function Parent() {
    this.name = 'parent1';
    this.play = [1, 2, 3];
    this.sayHei = function(){
        console.log("Hei~我是:" + this.name)
    }
  }
Parent.prototype.getFun = function(){
    console.log("你找到了这个方法!输出为:"+this.play)
}

 

一:原型链式继承

  实现方案:

function ChildOne() {
    this.type = 'childOne'; 
}
// 用.prototype 实现原型链式继承

ChildOne.prototype = new Parent(); 

var c1 = new ChildOne()

console.log(c1.name)
// 'parent1'

console.log(c1.play)
// [1, 2, 3]
console.log(c1.sayHei) // 'Hei~我是:parent1'
console.log(c1.getFun) // '你找到了这个方法!输出为:1,2,3'
 

缺点:

  1.原型中包含的引用值会在所有实例间共享  这是因为 在使用原型实现继承时,原型实际上变成了另一个类型的实例

var c2 = new ChildOne()

console.log(c2.play) // [1,2,3]

c2.play.push(4)

console.log(c2.play) // [1,2,3,4]

console.log(c1.play) // [1,2,3,4]
console.log(ChildOne.prototype.play) // [1,2,3,4]

// 由上可见,ChildOne原型上的play属性,都被改了

 

  2.子类型在实例化时不能给父类型的构造函数传参

 

二:构造函数式继承

  实现方案

function ChildTwo (){
// 借助call 来调用Parent函数
    Parent.call(this)
    this.type = 'child'
}
var t1 = new ChildTwo()
var t2 = new ChildTwo()

console.log(t1.name) // 'parent1'  

console.log(t1.play) // [1, 2, 3]

console.log(t1.sayHei) // 'Hei~我是:parent1'

t2.play.push(4)

console.log(t2.play) // [1,2,3,4]

console.log(t1.play) // [1,2,3]

缺点:

只能继承父类的实例属性和方法,不能访问父类原型上定义的方法

console.log(t1.getFun) // Uncaught TypeError: t1.getFun is not a function

console.log(t2.getFun) // Uncaught TypeError: t2.getFun is not a function

 

三:组合式继承 (把原型链式继承和构造函数式继承的优点结合。基本的思路是:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。)

实现方案:

function ChildThree() {
// 继承属性 Parent.call(
this) }
// 继承方法 ChildThree.prototype
= new Parent()

var s1 = new ChildTwo()
var s2 = new ChildTwo()

console.log(s1.name) // 'parent1'  

console.log(s1.play) // [1, 2, 3]

console.log(s1.sayHei) // 'Hei~我是:parent1'
console.log(s1.getFun) // '你找到了这个方法!输出为:1,2,3'
 s2.play.push(4)

console.log(s2.play) // [1,2,3,4]

console.log(s1.play) // [1,2,3]

完美的解决了上面的问题,唯一的缺点就是 Parent执行了两次【Parent.call(this) 和 new Parent()】,造成了多构造一次的性能开销

 

以下是实现对普通对象的继承

let parent = {
    name: "papa",
    friends: ["p1", "p2", "p3"],
    getName: function() {
      return "你好,我是:"+ this.name;
    }
  };

 四:原型式继承

用Object.create()实现,这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)

想更详细的了解Object.create() 点这里

  实现方案

var child1 = Object.create(parent)

var child2 = Object.create(parent)

console.log(child1.name)  // "papa"

console.log(child1.friends)  // ["p1", "p2", "p3"]
console.log(child1.getName()) // "你好,我是:papa"

 缺点:因为Object.create方法实现的是浅拷贝,  所以和原型链式继承一样  多个实例的引用类型属性指向相同的内存,存在篡改的可能

var child2 = Object.create(parent)
console.log(child2.friends)  // ["p1", "p2", "p3"]
child2.friends.push("p4")
console.log(child2.friends)  // ["p1", "p2", "p3","p4"]
console.log(child1.friends)  // ["p1", "p2", "p3","p4"]

 

五:寄生式继承

创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象

实现方案

function clone(origin){
    let clone = Object.create(origin)
    clone.getFriends = function(){
        return this.friends;
    }
    return clone
}
var j1 = clone(parent)
console.log(j1.name)  // "papa"

console.log(j1.friends)  // ["p1", "p2", "p3"]
console.log(j1.getName()) // "你好,我是:papa"

缺点跟原型式继承一样 

 六:寄生组合式继承

function clone (parent, child) {
    // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
    child.prototype = Object.create(parent.prototype);
}

function Parent() {
    this.name = 'parent1';
    this.play = [1, 2, 3];
    this.sayHei = function(){
        console.log("Hei~我是:" + this.name)
    }
  }

Parent.prototype.getFun = function(){
    console.log("你找到了这个方法!输出为:"+this.play)
}

function Child() { 
    Parent.call(this); 
}
 clone(Parent, Child);

var f1 = new Child()
var f2 = new Child()
console.log(f1.name) // 'parent1'  

console.log(f1.play) // [1, 2, 3]

console.log(f1.sayHei) // 'Hei~我是:parent1'

console.log(f1.getFun) // '你找到了这个方法!输出为:1,2,3'
console.log(f2.play) // [1, 2, 3]
f2.play.push(4)
console.log(f2.play) // [1, 2, 3,4]
console.log(f1.play) // [1, 2, 3]

属性都得到了继承,方法也没问题 【这种方式是较优的解决继承的方式】

注:Es6 里  Class 可以通过extends关键字实现继承,跟寄生组合继承的方式基本类似

 

参考链接:

1. https://vue3js.cn/interview/JavaScript/inherit.html#%E4%B8%80%E3%80%81%E6%98%AF%E4%BB%80%E4%B9%88

2. https://juejin.cn/post/7144903568649093156#heading-19