「JavaScript」js中的继承方法总结

1.前言

本文完整代码指路个人GitHub,欢迎star。js中的继承方式有不少种,如下仅列出部分。git

2.原型链继承

代码以下:github

function Parent() {
    this.name = 'jchermy';
}

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

function Child() {

}

Child.prototype = new Parent();

var child1 = new Child();
console.log(child1.getName()); //jchermy

这样看来貌似能够完美完成继承,而后当属性换成引用类型时,就会出现问题了,以下:函数

function Parent() {
    this.names = ["aa", "bb"]; //引用类型值
}

function Child() {

}

Child.prototype = new Parent();

var child1 = new Child();
child1.names.push("cc");
console.log(child1.names); //["aa","bb","cc"]

var child2 = new Child();
console.log(child2.names); //["aa","bb","cc"]

child2.names.push("dd");
console.log(child1.names) //["aa", "bb", "cc", "dd"]
console.log(child2.names);//["aa", "bb", "cc", "dd"]

var p = new Parent();
console.log(p.names); //["aa", "bb"]

由此咱们能够得出原型链继承的缺点:this

  1. 引用类型的属性被全部实例共享
  2. 在建立Child实例时,不能向Parent传参

2.借用构造函数继承

function Parent() {
    this.names = ["aa", "bb"];
}

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

var child1 = new Child();
child1.names.push("cc");
console.log(child1.names);//["aa", "bb", "cc"]

var child2 = new Child();
console.log(child2.names);//["aa", "bb"]

child2.names.push("dd");
console.log(child1.names); //["aa", "bb", "cc"]
console.log(child2.names); //["aa", "bb", "dd"]

var p = new Parent();
p.names; //["aa", "bb"]

能够看出,借用构造函数继承避免了一下原型链继承的问题,主要体如今:spa

  1. 避免了引用类型的属性被全部实例共享
  2. 能够在Child中向Parent传参

然而,借用构造函数继承也有缺点。prototype

function Parent(name) {
    this.name = "parent "+name;
}

function Child(name) {
    this.name = "child"+name;
    Parent.call(this, name);
}

var child1 = new Child('hemin');
console.log(chil1.name); //"parent hemin"

var child2 = new Child("aa");
console.log(child2.name); //"parent aa"

缺点:方法都在构造函数中定义,每次建立实例都会建立一遍方法code

3.组合继承

组合继承融合原型链继承和构造函数的优势,是JavaScript中最经常使用的继承模式对象

function Parent(name) {
    this.name = name;
    this.colors = ["red", "blue"];
}

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

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

Child.prototype = new Parent();
Child.prototype.constructor = Child;

var child1 = new Child("aa", 18);
child1.colors.push("black");

child1.name; //"aa"
child1.age; //18
child1.colors; //["red", "blue","black"]

var child2 = new Child("bb", 20);
child2.name; //"bb"
child2.age; //20
child2.colors; //["red", "blue"]

然而组合继承也有一个缺点,就是会调用两次父构造函数。blog

以下:继承

Child.prototype = new Parent(); 
var child1 = new Child('aa', '18');

因此,在这个例子中,若是咱们打印 child1 对象,咱们会发现 Child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue']。
图片描述

这个问题咱们在下面再讨论。

4.原型式继承

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

var person = {
    name: 'jchermy',
    friends: ["aa", "bb"]
}

var person1 = createObj(person);
var person2 = createObj(person);

//注意:修改person1.name的值,person2.name的值并未发生改变,
//并非由于person1和person2有独立的 name 值,而是由于person1.name = 'person1',给person1添加了 name 值,并不是修改了原型上的 name 值。
person1.name = "xiaomi";
console.log(person2.name); //"jchermy"

person2.friends.push("cc");
console.log(person1.friends); //["aa", "bb", "cc"]

缺点:包含引用类型的属性值始终会共享相应的值,与原型链继承同样

5.寄生式继承

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

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

var person = {
    name: "jchermy",
    friends: ["aa", "bb"]
};

var person1 = createObj(person);
var person2 = createObj(person);

person1.name = "xiaomi";
console.log(person1.name); //"xiaomi"
console.log(person2.name); //"jchermy"

person1.friends.push("xxx");
console.log(person1.friends); // ["aa", "bb", "xxx"]
console.log(person2.friends); // ["aa", "bb", "xxx"]

缺点:

  1. 跟借用构造函数模式同样,每次建立对象都会建立一遍方法
  2. 包含引用类型的属性值始终会共享相应的值

6.寄生组合式继承

还记得组合继承中提到的那些问题吗,那么咱们该如何精益求精,避免这一次重复调用呢?

若是咱们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?能够这样实现:

function Parent(name) {
   this.name = name;
   this.colors = ["red", "blue", "green"];
}

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();


  
  Child.prototype.constructor = Child;

  var child1 = new Child('xiaomi', 18);
  var child2 = new Child2('aa', 24);
  console.log(child1.name); //xiaomi
  console.log(child2.name); //aa

  child1.colors.push("black");
  child1.colors; //["red", "blue", "green", "black"]
  child2.colors; //["red", "blue", "green"];
这种方式的高效率体现它只调用了一次 Parent 构造函数,而且所以避免了在 Parent.prototype 上面建立没必要要的、多余的属性。与此同时,原型链还能保持不变;所以,还可以正常使用 instanceof 和 isPrototypeOf。开发人员广泛认为寄生组合式继承是引用类型最理想的继承范式。

7.结语

若是文章对你有帮助的话,欢迎点赞收藏!!有错误和不合理的地方欢迎你们指正!GitHub给个star就最好啦!=(//▽//)感谢你们~

相关文章
相关标签/搜索