原型链继承

最常见的继承形式,波及构造函数,原型和实例

  1. 每一个构造函数都有一个原型对象
  2. 原型对象又蕴含一个指向构造函数的指针
  3. 而实例则蕴含一个原型对象的指针
    这种继承形式存在问题

    function Parent(){ this.name="parent" this.own = [1,2,3]}function Child(){ this.type ="child"}Child.prototype = new Parent()console.log(new Child())  // Parent { type: 'child' }let c1 = new Child();let c2 = new Child();c1.own.push(4)console.log(c1.own,c2.own) // [ 1, 2, 3, 4 ] [ 1, 2, 3, 4 ]

    因为两个实例应用同一个原型对象,内存空间共享,当一个发生变化的时候,另外一个也随之进行了变动

构造函数继承

借助call实现

function Parent(){    this.name="parent"    this.own = [1,2,3]}Parent.prototype.getName = function () {    return this.name;  }function Child(){    Parent.call(this)    this.type ="child"}const child = new Child()console.log(child)  // Child { name: 'parent', own: [ 1, 2, 3 ], type: 'child' }console.log(child.getName())   // child.getName is not a function

这种形式解决了原型共享内存的弊病,随之也带来了新的问题。只能继承父类的实例属性和办法,不能继承原型属性或者办法。

组合继承

这种形式联合了前两种继承形式的优缺点,联合起来的继承

function Parent(){    this.name="parent"    this.own = [1,2,3]}Parent.prototype.getName = function () {    return this.name;  }function Child(){    // 第二次调用    Parent.call(this)    this.type ="child"}// 第一次调用 Parent3()Child.prototype = new Parent()// 手动挂上结构器,指向本人的构造函数Child.prototype.constructor = Child;let c1 = new Child()let c2 = new Child()c1.own.push(4)console.log(c1.own,c2.own)  // [ 1, 2, 3, 4 ] [ 1, 2, 3 ]console.log(c1.getName())  // parentconsole.log(c2.getName())  // parent

Parent执行了两次,第一次是扭转Child 的 prototype 的时候,第二次是通过 call 办法调用 Parent 的时候,那么 Parent 多结构一次就多进行了一次性能开销

原型式继承

应用ES5 外面的 Object.create 办法,这个办法接管两个参数:一是用作新对象原型的对象、二是为新对象定义额定属性的对象(可选参数)

let parent = {    name:"parent",    friend:['1',',2','3'],    getName:function(){        return this.name    }}let p1 = Object.create(parent)p1.name ="jake"p1.friend.push("a")let p2 = Object.create(parent)p1.friend.push("b")console.log(p1.name)  // jakeconsole.log(p1.name === p1.getName())   // trueconsole.log(p2.name) // parentconsole.log(p1.friend) // [ '1', ',2', '3', 'a', 'b' ]console.log(p2.friend) // [ '1', ',2', '3', 'a', 'b' ]

这种继承形式的毛病很显著,多个实例的援用类型属性指向雷同的内存,存在篡改的可能

寄生式继承

原型式继承能够取得一份指标对象的浅拷贝,而后利用这个浅拷贝的能力再进行加强,增加一些办法,这样的继承形式就叫作寄生式继承
优缺点和原型式继承一样,然而对于一般对象的继承形式来说,寄生式继承相比于原型式继承,在父类根底上增加了更多的办法

let parent = {    name:"parent",    friend:['1',',2','3'],    getName:function(){        return this.name    }}function clone(src) {    let clone = Object.create(src);    clone.getFriends = function() {      return this.friend;    };    return clone;  }let p1 =clone(parent)console.log(p1.getName())  // parentconsole.log(p1.getFriends())   // [ '1', ',2', '3' ]

减少了 getFriends 的办法,从而使 p1 这个一般对象在继承过程中又减少了一个办法,仍然存在两次调用父类的构造函数造成节约

寄生组合式继承

解决前几种继承形式的毛病,较好地实现了继承想要的后果,同时也缩小了结构次数,缩小了性能的开销

function clone(parent, child) {    // 这里改用 Object.create 就能够缩小组合继承中多进行一次结构的过程    child.prototype = Object.create(parent.prototype)    child.prototype.constructor = child;}function Parent() {    this.name = "parent"    this.friend = ['1', ',2', '3']}Parent.prototype.getName = function () {    return this.name}function Child() {    Parent.call(this)    this.friend = "labor"}clone(Parent, Child)Child.prototype.getFriends = function(){    return this.friend}let c1 = new Child()console.log(c1)  // Child { name: 'parent', friend: 'labor' }console.log(c1.getName())  // parentconsole.log(c1.getFriends()) // labor

ES6中的extends也采纳这种形式

归类
不应用Object.create的形式构造函数继承,原型链继承,他们组合成为组合继承
应用Object.create的形式原型式继承,寄生式继承,依此为根底有了寄生组合式继承,和ES6 extends相似