JS是一门面向对象的语言,可是在JS中没有引入类的概念,以前特别疑惑在JS中继承的机制究竟是怎样的,一直学了JS的继承这块后才恍然大悟,遂记之。数组
假如如今有一个“人类”的构造函数:app
咱们能够经过在子类型的内部调用超类型的构造函数来达到子类型继承超类型的效果。函数是在特定环境中执行的代码对象,所以能够经过call()或者apply()在新建立的对象上执行构造函数。函数
不过借用构造函数进行继承,不免会有方法都在构造函数中定义,没法实现函数的复用。而且在超类型的原型中定义的方法对于子类型是不可见的,结果全部类型都要使用构造函数进行继承,因此单独使用构造函数的状况比较少。this
原型链的定义机制我在JavaScript中原型链的那些事中已经提过了,说到底,经过原型链实现继承根本是经过prototype属性进行实现的。spa
若是Man的原型指向的是Human的实例,那么Man原型对象中将会包括一个指向Human的指针,那么全部Man的实例就均可以继承Human了。.net
咱们改变了Man的prototype的指向,让他等于一个Human的实例对象。即:指针
组合继承的总体思想就是将原型链和借用构造函数同时使用,取二者的长处的一种继承模式。思路是使用原型链实现原型属性和方法的继承,借用构造函数来实现对实例属性的继承。这样作的好处是实现了函数的复用,同时又保证了每一个属性都有本身的属性。code
那么上面让“男人”继承“人类”就能够经过组合继承实现:对象
若是说继承的对象并非构造函数呢?咱们没有办法使用借用构造函数进行继承,这个时候咱们就可使用原型式继承。
这个继承模式是由道格拉斯·克罗克福德提出的。原型式继承并无使用严格意义上的构造函数。而是借助原型能够在已有的对象上建立新的对象,同时还避免了建立自定义类型。因此道格拉斯·克罗克福德给出了一个函数:
咱们能够建立一个新的临时性的对象来保存超类型上全部属性和方法,用来给子类型的继承。而这个就是这个函数要作的事。
在object函数内部先建立了一个临时性的构造函数,而后将传入的对象做为这个构造函数的原型,最后返回这个临时类型的新实例。本质上其实就是object对传入的对象进行了一次浅复制。
如今有一个“男人”对象:
这里须要注意的是,这两个对象如今时普通的对象,而不是构造函数,因此咱们没法使用上面的方法。
咱们能够经过原型式继承,以下面的例子:
在ECMAScript中经过函数Object.create()规范了原型式继承,该方法接收两个参数,一个用于新对象原型的对象,第二个参数用于为新对象定义额外属性的对象,在传入一个参数的状况下,Object.create()和上面的object()函数做用相同。
咱们能够想一下其实继承的意思就是子类型把超类型的全部属性和方法拿过来放在本身身上。 那么咱们能够将超类型的属性和方法所有拷贝给子类型,从而实现继承。
咱们能够实现一个方法,将超类型的对象传入方法,而后将对象的属性添加到子类型上并返回,具体代码以下:
具体使用能够这样:
使用方法相似于上面介绍的原型式继承。可是这样实现继承有一个很大的问题,那就是当对象的属性是引用类型值(数组,对象等)时,在拷贝过程当中,子对象得到的只是一个内存地址,而不是真正的属性拷贝。
咱们能够在浅拷贝的基础上进行深拷贝。咱们知道,当在拷贝基本类型值时是在内存中新开辟了一块区域用于拷贝对象属性的存储,因此咱们只须要递归下去调用浅拷贝就好了。
使用方法和浅拷贝相似,这里就不举例了。
寄生式继承的思路就是建立一个用于封装继承过程的函数,在该函数内部以某种方式来加强对象,最后再向真的它作了全部工做同样返回对象。仍是上面man和human两个对象间实现继承的例子:
在主要实现对象是自定义类型而不是构造函数的状况下,寄生式继承是一种有用的继承模式,其中使用的object函数不是必须的,任何能实现该功能的函数均可以。
组合继承是JS中一种很是经常使用的继承模式,但是这个方式实现继承有一个问题,就是不管在任何状况下,都会调用两次超类型构造函数。一次是在建立子类型原型的时候,第二次是在子类型构造函数内部。子类型最终会包含超类型对象的所有实例属性,可是咱们不得不在调用子类型构造函数时重写这些属性。如此在继承很是频繁的状况下就会形成内存过分损耗的状况了。这个时候,咱们可使用寄生组合式继承!
寄生组合式继承,就是借用构造函数来继承属性,经过原型链的混成形式来继承方法。具体思路是没必要为了指定子类型的原型而调用超类型的构造函数,咱们所须要的无非就是超类型原型的一个副本而已,本质上,就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。
基本模式以下:
让咱们回到第一个问题:有一个“男人”的构造函数和“人类”的构造函数,我如今想让男人继承人类!
以上~