ES 中只支持实现继承,并且其实现继承主要依靠原型链来实现的。编程
ES中 描述了 原型链的概念,并将原型链做为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。bash
每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如咱们让原型对象等于另外一个类型的实例。那么此时的原型对象将包含一个指向另外一个原型的指针,相应地,另外一个原型中也包含着一个指向另外一个构造函数的指针。app
另外,假如另外一个原型又是另外一个类型的实例,如此层层递进,就构成了实例与原型的链条。这就是原型链的基本概念。函数
function SuperType(){
this.property = true
}
SuperType.prototype.getSuperValue = function(){
retrun this.property
}
function SubType(){
this.subproperty = false
}
// 继承了 SuperType
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function(){
retrun this.subproperty;
}
var instance = new SubType()
instance.getSuperValue() // true
复制代码
SuperType 和SubType。每一个类型分别有一个属性和一个方法。它们的主要区别是:测试
SubType继承了SuperType,而继承是经过建立SuperType的实例,并将该实例赋给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。ui
也就是说,原来存在于SuperType的实例中的全部方法和属性,如今也存在于SubType.prototype中。在给SubType.prototype添加一个方法后,这样就在继承了SuperType的属性和方法的基础上又添加了一个新的方法。这样就实现了实例以及构造函数和原型之间的关系。this
经过实现原型链,本质上扩展了原型搜索机制,当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。若是没有找到该属性,则会继续搜索实例的原型。在经过原型链实现继承的状况下,搜索过程就得以沿着原型链继续向上。直到最后一步找到该方法。在找不到属性和方法的状况下,搜索过程老是要一环一环地前行到原型链末端才会停下来。spa
全部引用类型默认都继承了Object,而这个继承也是经过原型链实现的。全部函数的默认原型都是Object的实例 所以默认原型都会包含一个内部指针,指向 Object.prototype。这也是全部自定义类型都会继承 toString()、valueOf()默认方法的缘由prototype
能够经过两种方式来肯定原型和实例之间的关系。第一种方式就是使用 instanceof操做符,只要用这个操做符来测试实例与原型链中出现过的构造函数,结果就会返回 true。以下:指针
instance instanceof Object // true
复制代码
第二种方法就是 isPrototypeOf() 方法,一样,只要原型链中出现过的原型,均可以说是该原型链所派生的实例原型,所以 isPrototypeOf() 方法也会返回 true
Object.prototype.isPrototypeOf(instance) // true
复制代码
子类型有时候须要重写超类型中的某个方法,或者须要添加超类型中不存在的某个方法,可是不管如何,给原型添加方法的代码必定要放在替换原型的语句以后
function SuperType() {
this.property = true
}
SuperType.prototype.getSuperValue = function(){
retrun this.property
}
function SubType() {
this.subproperty = false
}
----
// 继承了SuperType
SubType.prototype = new SuperType()
// 添加新方法
SubType.prototype.getSubValue = function(){
retrun this.subproperty
}
//重写超类型中的方法
SubType.prototype.getSuperValue = function(){
retrun false
}
-----
var instance = new SubType()
instance.getSuperValue() // false
复制代码
第一个方法 getSubValue()被添加到了SubType中,第二个方法 getSuperValue()是原型链中已经存在的一个方法,但重写这个方法将会屏蔽原来那个方法.
换句话说,当 SubType 的实例调用 getSuperValue()时,调用的就是这个从新定义的方法,但经过 SuperType的实例调用 getSuperValue()时,还会继续调用原来的那个方法,全部必需要在用SuperType的实例替换原型以后,在定义这两个方法。
注意:即在经过原型链实现继承的时候,不能使用对象字面量建立原型方法,由于这样作会重写原型。
一、来自包含引用类型值的原型。在以前说过包含引用类型值的原型属性会被全部实例共享。因此这也是为何要在构造函数中,而不是在原型对象中定义属性的缘由。在经过原型来实现继承时,原型实际上会变成另外一个类型的实例。因而,原先的实例属性也就瓜熟蒂落地变成了如今的原型属性。
二、在建立子类型的实例时,不能向超类型的构造函数中传递参数。实际上能够说是没有办法再不影响全部对象实例的状况下,给超类型的构造函数传递参数a。
利用在子类型构造函数的内部调用超类型构造函数。便可以经过 apply() 和 call()方法在新建立的对象上执行构造函数。
相对于原型链而言,借用构造函数有一个很大的优点,便可以在子类型构造函数中向超类型构造函数传递参数。
function s(name) {
this.name = name
}
function b() {
// 继承 s,同时还传递参数
s.call(this, 'nnn')
// 实例属性
this.age = 23
}
let i = new B()
i.name // nn
i.age // 29
复制代码
若是仅仅是借用构造函数,那么也就没法避免构造函数模式存在的问题,方法都在构造函数中定义,所以函数复用就没法实现。另外,在超类型的原型中定义的方法,对子类型而言也是不可见的。结果全部类型都只能使用构造 函数模式。因此借用构造函数模式不多单独使用。
也叫作伪 经典继承,指的是将原型链和借用构造函数的技术组合到一块。发挥两者的长处的一种继承模式。原理就是使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承。
借助已有的对象建立新对象,先建立一个临时的构造函数,而后将传入的对象做为这个构造函数的原型,最后返回这个临时类型的新实例。
建立一个仅用于封装继承过程的函数,该函数在内部以某种方式加强对象,而后再返回对象。一样也是不能作到函数复用而会下降效率
ES 支持面向对象编程,但不使用类或接口。对象能够在代码执行过程当中建立和加强,所以具备动态性而非严格定义的实体。在没有类的状况下,能够采用 工厂模式、构造函数模式、原型模式建立对象。
使用简单的函数建立对象,为对象添加属性和方法,而后返回对象。这个模式被构造函数模式所取代
建立自定义引用类型,能够像建立内置对象实例同样使用 new 操做符。不过,构造函数模式也有缺点,即它的每一个成员没法获得复用,包括函数。因为函数能够不局限于任何对象。所以没有理由再也不多个对象之间共享函数。
使用构造函数的 prototype 属性来指定那些应该共享的属性和方法。组合使用构造函数模式和原型模式时,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。
JS主要经过原型链实现继承。原型链的构建是将一个类型的实例赋值给另外一个构造函数的原型实现。这样,子类型就可以访问超类型的全部属性和方法。这点和基于类的继承很类似。
原型链的问题就是对象实例共享全部继承的属性和方法,所以不适合单独使用。 若是想解决这个问题就须要借助于构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就能够作到每一个实例都具备本身的属性,同时还能保证只使用构造函数模式来定义类型。
能够在不预先定义构造函数的状况下实现继承,其本质是执行对给定对象的浅复制。而复制获得的副本还能够进一步改造
与原型式继承很是类似,也是基于某个对象或某些信息建立一个对象,而后加强对象,最后返回对象。为了解决组合继承模式因为屡次调用超类型构造函数而致使低效率问题,能够将这个模式和组合继承一块儿使用
集寄生式继承和组合继承的优势于一身,是实现基于类型继承的最有效方式