说道这个继承,了解object-oriented的朋友都知道,大多oo语言都有两种,一种是接口继承(只继承方法签名);一种是实现继承(继承实际的方法)
奈何js中没有签名,因而只有实现继承,而且靠的是原型链实现的。下面正式的说一说js中继承那点事儿
1、原型链原型链:实现继承的主要方法,利用原型让一个引用类型继承另一个引用类型的属性和方法。
回顾:构造函数,原型,实例三者的关系
每一个构造函数都有一个原型对象(Person.prototype);原型对象都包含指向构造函数的指针(constructor);每个实例都包含指向原型对象的指针(看不见的_proto_指针)
原型链是怎么来的呢?
某个构造函数的原型对象是另一个构造函数的实例;这个构造函数的原型对象就会有个(看不见的_proto_指针)指向另一个构造函数的原型对象;
那么另一个原型对象又是其他的构造函数实例又会怎么样,就这样层层递进,形成原型链;来具体看一下吧
//第一个构造函数;有一个属性和一个原型方法 function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function(){ return this.property } //第二个构造函数;目前有一个属性 function SubType(){ this.subproperty=false } //继承了SuperType;SubType原型成了SuperType的实例;实际就是重写SubType的原型对象;给SuperType原型对象继承了 SubType.prototype=new SuperType() //现在这个构造函数有两个属性(一个本身的subproperty,一个继承的存在原型对象的property);两个方法(一个原型对象的getSubValue,一个原型对象的原型对象的getSuperValue) SubType.prototype.getSubValue=function(){ return this.subproperty } var instance=new SubType() //创建第二个构造函数的实例 console.log(instance.getSuperValue()) //true 先查找instance这个实例有没有此方法;显然没有,再查找SubType原型对象有没有此方法;也没有,再查找SubType原型对象的原型对象;显然是存在的
注意:instance的constructor现在指向的是SuperType这个构造函数;因为原来的SubType.prototype被重写了,其内部的constructor也就随着SubType.prototype的原型对象的constructor指向构造函数SuperType;至于原型搜索机制是怎么样运行的,请仔细看上面的代码,相信你是可以的
1.1完整的原型
在原型那节已经提了些,还是再说一下。完整的原型包括Object。
所有函数的默认原型都是Object的实例;每个默认原型都有个_proto_指针指向Object.prototype;因此自定义类型都继承如toString,valueOf的方法
而Object.prototype的_proto_指针指向null来结束原型链。以Person构造函数为例,看看完整的原型链图
1.2原型和实例的关系判断
第一种使用instanceof操作符: 测试实例和原型链中出现的构造函数,结果为true
第二种使用isPrototypeOf()方法: 只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型
console.log(instance instanceof Object) //都为true console.log(instance instanceof SuperType) console.log(instance instanceof SubType) console.log(Object.prototype.isPrototypeOf(instance)) //都为true console.log(SuperType.prototype.isPrototypeOf(instance)) console.log(SubType.prototype.isPrototypeOf(instance))
1.3谨慎定义方法
注意:给原型对象添加方法,一定放在替换原型的后面,因为放在替换原型之前是找不到了,原型会被重写的;
注意:在通过原型链继承时,不能使用对象字面量创建原型方法,因为也会重写原型链;
function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function(){ return this.property } function SubType(){ this.subproperty=false } //继承SuperType SubType.prototype=new SuperType() //使用字面量添加新方法,导致上一行无效 因为现在的原型替换了Object实例而非SuperType的实例,关系中断 SubType.prototype={ getSubValue:function(){ return this.subproperty; }, somOtherMethod:function(){ return false } }; var instance=new SubType() console.log(instance.getSuperValue()) //error
1.4原型链的问题
1、包含引用类型值的原型:当实例是另一函数的原型时,引用类型值就会变成原型上的属性,就会被另一函数的实例所共享。
function SuperType(){ this.colors=["yellow","red","olive"] } function SubType(){ } SubType.prototype=new SuperType() //color实际上就是原型上的了 var instance1=new SubType() instance1.colors.push("purple") var instance2=new SubType() console.log(instance1.colors==instance2.colors) //true
2、创建子类型实例时,不能向超类型的构造函数传递参数(没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)
2、借助构造函数为了解决原型中包含引用类型值带来的问题,利用构造函数来解决
在子类型构造函数的内部调用超类型构造函数(函数是特定环境中执行代码的对象,可以通过apply或call调用)