我们继续来造猴子;
1,原型链继承
function Monkey(){ this.name="wukong"; this.eye="fireGoldEyes" }; Monkey.prototype.attcak=function(){ alert(this.name+"一棒造成了成吨的伤害") }; function Mokey_huaguoshan(){ this.view="72views" } Mokey_huaguoshan.prototype=new Monkey() Mokey_huaguoshan.prototype.run=function(){ alert(this.name+"runing 108000KM") } var sunxingzhe=new Mokey_huaguoshan();
上面就是一只花果山的猴子构造函数继承了 猴子的构造函数;最后通过花果山的猴子生成了孙行者;
猴子构造函数被称作超类型 构造函数;花果山的猴子被成为子类型的构造函数; 孙行者就是实例;
以上的通过这一步
Mokey_huaguoshan.prototype=new Monkey() // 通过类似生成实例 的方式来实现继承;
Mokey_huaguoshan.prototype.__proto__=== Monkey.prototype; // true这样看的更清楚了 实例的__proto__指向了构造函数的prototype;
这样通过改写 构造函数的prototype属性来实现继承焦作原型链继承;
这边的孙行者的__proto__本应该指向 Mokey_huaguoshan.prototype;
但是Mokey_huaguoshan.prototype被改写;
我们看到Mokey_huaguoshan.prototype.__proto__指向了 Monkey.prototype;
所以呢 这边的sunxingzhe.__proto__.__proto__===Monkey.prototype;
对应的 sunxingzhe.__proto__.__proto__.constructor===Monkey;//就是sunxingzhe.constructor===Monkey;
PS:我们在consonle.dir( FUN);打印出来的函数属性里 ,
prototype属性表明了函数的原型对象,
__proto__则代表这函数的个prototype原型对象的指向位置;
注意事项:1,不要在继承过以后用字面对象去重新定义构造函数的prototype属性,这样会切断原型链 ,报error;
2,改写构造函数的方法要放在继承操作以后;
缺点: 1,很明显。他和原型构造函数一样不能入参给超类型的构造函数;没有入口;
2,很明显。他和原型构造函数一样控制以后修改属性是引用类型的bug;
很少会单独用这种方法;
2, 经典继承(call,apply )
又名借用构造函数继承和伪造对象继承;
function Monkey(){ this.name="wukong"; this.eye=["fireGoldEyes"] }; Monkey.prototype.attcak=function(){ alert(this.name+"一棒造成了成吨的伤害") }; function Mokey_huaguoshan(){ Monkey.call(this); } var sunxingzhe=new Mokey_huaguoshan();
var bimawen=new Monket_huaguoshan();
call,apply这中是可以入参的,在call(this, 参数位置);call入一个值;apply入进去一个数组;
这样就解决了遗留的入参问题;
sunxingzhe.eye.push( "Km Of Eyes");
console.log(sunxingzhe.eye) // [ "fireGoldEyes", "Km Of Eyes"]
console.log(bimawen.eye) // [ "fireGoldEyes"]
这是由于子类型构造函数在生成实例的时候 call或者apply时候 调用了 超类型构造函数的初始值;并且生成了自己的一个副本;
这样就解决了遗留的属性为引用属性问题;
缺点:1,超类型定义的方法对子类型不可见;继承不到(只能内部定义);
2,方法都必须在构造函数内部定义,复用导致内存增加,效率低(构造函数的缺点);
很少会单独用这种方法;
3 ,组合式继承 (原型链继承+经典继承) 经常用的
又被称作伪经典继承;
function Monkey(){ this.name="wukong"; this.eye=["fireGoldEyes"] }; Monkey.prototype.attcak=function(){ alert(this.name+"一棒造成了成吨的伤害") }; function Mokey_huaguoshan(){ Monkey.call(this); } Mokey_huaguoshan.prototype=new Monkey(); Mokey_huaguoshan.prototype.constructor=Monkey_huaguoshan; Monkey_huaguoshan.prototype.run=function(){ alert (this.name+"runing 108000KM") } var sunxingzhe=new Mokey_huaguoshan(); var bimawen=new Monket_huaguoshan();
其中心思想是 借用原型链把原型上的方法属性继承过来;在利用经典继承对实例属性进行修正。
这样就比较完美了;
而且造出的对象可以被 instanceof和isPrototypeOf( )的方法所检测到;
下面的两种方法是一对儿
4,原型继承;
首先放出 他的api
ES5的规范 Object.create();
用法 例子
用这种方法首先你的有个对象;
var Monkey={
name:"wukong"
}
var Mokey_huaguoshan = Object.create(Monkey,{
age:{
value:10000000
};
} )
解释一下 参数 第一个参数是 源对象 ;
第二个参数是 对象上的属性和方法 写法和defineProperty()一样;
我们来研究一下他的源码 Object.create()
其实下面就是他的实现;
function Object_create( o ){
function F(){};
F.prototype=o;
return new F();
}
我们知道 参数 o 其实是 arguments的一个拷贝(引用类型前拷贝,基本类型深拷贝);这边是对象 所以是前拷贝;
所以只是一个引用,那么生成的实例属性如果是对于源对象的一个引用数据类型的话那么 就存在和原型继承一样的 引用数据类型属性问题; so大家一定要注意啊;
使用这个API 一般来说只是 一个对象和另外一个对象保持相似是足够用了
5, 寄生式继承
我个人觉得和原型继承是一对 ; 原型继承去实现属性;寄生去实现方法;
在此原型继承API的基础上 我们写一下实现
function create_func( obj){
var monkey = Object.create( obj);
monkey.run=function (){
alert ( this.name+"runing 108000KM")
}
return monkey;
}
var sunxingzhe=create_func(Monkey);
这种方法 也有和构造函数一样的缺点;就是说实例上的函数每次都重新New 一下 从而导致效率降低;