- 通过原型链来体现继承的思想,如下面两个对象a和b:
1 function a(){ 2 this.aProperty=true; 3 } 4 function b(){ 5 this.bProperty=false; 6 } 7 a.prototype.getAvalue=function(){ 8 return this.aProperty; 9 } 10 b.prototype.getBvalue=function(){ 11 return this.bProperty; 12 } 13 b.prototype.myFunction=function(){ 14 return "B_function"; 15 } 16 a.prototype.myFunction=function(){ 17 return "A_function"; 18 } 19 20 b.prototype=new a();//一旦将b的原型指向a的实例 21 var instance=new b();//构建一个b的实例 22 console.log(instance.getAvalue());//true, 返回a的属性 23 // console.log(instance.getBvalue());//Error 24 console.log(instance.myFunction());//B_function
按照书上所说 “换句话说,原来存在于SuperType (a)的实例中的所有属性和方法,现在也存在于SubType.prototype (b.prototype)中了。在确立了继承关系之后,我们给SubType.prototype(b.prototype) 添加了一个方法(即getBvalue),这样就在继承了SuperType(a) 的属性和方法的基础上又添加了一个新方法”
其实经过尝试,虽然就算给b的prototype添加了新的方法,只要b的prototype指向a的实例,那么这个instance始终无法调用自身原型内的方法,即:
1 console.log(instance.getBvalue());//Error
始终无法调用getBvalue()方法。
所以我又尝试分别在a和b的原型中定义两个方法,果然不出所料
1 a.prototype.myFunction=function(){ 2 return "A_function"; 3 } 4 b.prototype.myFunction=function(){ 5 return "B_function"; 6 } 7 console.log(instance.myFunction());//A_function
返回的是A原型的方法。
- 所有的对象,其默认原型都指向Object.prototype。相当于: obj.prototype=new Object() 。所以说,所有自定义的类型,都继承了Object原型内的方法,如toString(), valueOf()之类的。
那么问题就是,如果我在b或者a原型或者本身属性中覆写了toString 方法,那么调用谁的呢?
1 //原型中,覆写toString 2 a.prototype.toString=function(){ 3 return "over-write A_toString in prototype"; 4 } 5 b.prototype.toString=function(){ 6 return "over-write B_toString in prototyp"; 7 } 8 //本身属性中,覆写toString 9 a.toString=function(){ 10 return "over-write A_toString"; 11 } 12 b.toString=function(){ 13 return "over-write B_toString"; 14 } 15 //尝试调用一下toString 看返回什么 16 console.log(instance.toString())//over-write A_toString in prototype
发现是可以覆写Object中的toString,并通过实例来调用的。
- instanceof和isPrototypeOf()来判断原型和实例的关系
- 父类中的getAvalue可以在b的prototype中覆写
1 //覆写父类中的getAvalue 2 b.prototype.getAvalue=function(){ 3 return "over-write getAvalue"; 4 } 5 console.log(instance.getAvalue());//over-write getAvalue
与上述类似,是可以通过b的原型沿着原型链向上查找到getAvalue,并覆写的。但如果通过a的实例来调用,则不会使用被覆写的方法
1 console.log(new a().getAvalue());//true
- 关于调用getBvalue方法的调用问题:
之前一直以为自己想不到方法调用getBvalue了,因为总是出现Error,后来发现问题出在代码顺序上!!!!
如果在实现继承之前,定义了b的getBvalue,那么是无法调用的。但是如果在继承之后,再定义b的getBvalue,相当于添加一个新方法,这时候是可以调用的
1 b.prototype.getBvalue=function(){ 2 return this.bProperty; 3 } 4 console.log(instance.getBvalue());//false
添加的新方法,一定要在继承完成之后!
- 原型链的问题主要出在:当使用引用类型定义新的实例的时候,对一个实例的更改,会导致该引用类型内部字段的更改
1 function A(){ 2 this.color=['red','yellow','blue']; 3 } 4 function B(){ 5 6 } 7 B.prototype=new A()//继承 8 var f1=new B(); 9 f1.color.push('brown'); 10 console.log(f1.color);//["red", "yellow", "blue", "brown"] 11 var f2=new B(); 12 console.log(f2.color);// ["red", "yellow", "blue", "brown"]
所以我们定义属性的时候,一定要在构造函数中定义,不要在原型对象里面定义。这里因为B的原型中通过继承得到了A的color属性,结果就悲剧了
- 解决方法1是通过call和apply方法来借用构造函数(伪继承)
1 //solution 1 2 function A_1(){ 3 this.color=['red','yellow','blue']; 4 } 5 function B_1(){ 6 A_1.call(this);//伪继承,借用call来调用父类构造函数 7 } 8 //B_1.prototype=new A_1()//这个继承可以不用写了 9 var f_1=new B_1(); 10 f_1.color.push('brown'); 11 console.log(f1.color);//["red", "yellow", "blue", "brown"] 12 var f_2=new B_1(); 13 console.log(f_2.color);//["red", "yellow", "blue"]
相当于借用call来调用父类的构造函数
- 解决方法2是借助组合的方法,将构造函数和显式继承组合在一起实现继承
1 //组合继承 2 function flower_A(name){ 3 this.colors=['red','yellow']; 4 this.name=name 5 } 6 flower_A.prototype.sayName=function(){ 7 return this.name; 8 } 9 function flower_B(name,age){ 10 flower_A.call(this,"Jasmine");//通过call调用A,伪继承 11 this.age=age; 12 }
//显式继承 13 flower_B.prototype=new flower_A(); 14 flower_B.prototype.constructor=flower_B;//这里要将它的constructor改成flower_B
15 flower_B.prototype.sayAge=function(){ 16 return this.age; 17 } 18 var instace=new flower_B("Violet",20); 19 instace.colors.push('black'); 20 console.log(instace.colors)//["red", "yellow", "black"] 21 console.log(instace.sayAge());20// 22 var instace_2=new flower_B("Rose",18); 23 console.log(instace_2.colors)//["red", "yellow"] 24 console.log(instace_2.sayAge())//18 可以改成instance_age
- 解决方法3,不!是原型式继承,这个方法就像原型模式一样,新建一个实例并添加字段属性,会修改原字段。还有一个create方法,这里大致说一下,第一个参数是对象,第二个参数是其属性,属性包括value, enumerable, writable, configurable,get,set
1 //原型式继承,但并没什么卵用 2 function object(o){ 3 function f(){};//临时的构造函数f 4 f.prototype=o;//传入对象为构造函数的原型 5 return new f();//返回这个构造函数的实例 6 } 7 //该如何使用呢? 8 var person={ 9 name:'Jonathan', 10 age:20, 11 friends:['Emily',"Jing"] 12 } 13 //传统的创建对象 14 var somebody=new object(person); 15 somebody.name="Grey"; 16 somebody.friends.push('Van'); 17 //用ECAMScript5的方法 18 var somebody2=Object.create(person,{ 19 name:{ 20 value:"Alice" 21 } 22 }) 23 24 console.log(somebody.friends);//["Emily", "Jing", "Van", "Mike"] 25 console.log(somebody2.friends);//["Emily", "Jing", "Van", "Mike"] 26 console.log(somebody2.name)//Alice
- 寄生式的继承,类似于原型模式继承,同样是要创建object函数,比较麻烦
1 //寄生继承模式 2 function createAnother(original){ 3 var clone=new object(original); 4 clone.sayHi=function(){ 5 console.log("hi"); 6 } 7 return clone; 8 } 9 var createPerson={ 10 name:"Cage", 11 friends:['Emily',"Jing"] 12 } 13 var somebody3=new createAnother(createPerson); 14 somebody3.sayHi();//hi
- 解决方法3来了,这个依靠寄生组合式的继承,仍然需要一个object函数,主要优势在于相比于组合继承,它只调用了superType的构造函数一次。
1 //寄生组合继承 2 function AA(name){ 3 this.name=name, 4 this.colors=['red','yellow','brown'] 5 } 6 AA.prototype.sayName=function(){ 7 console.log(this.name) 8 } 9 10 function BB(name,age){ 11 AA.call(this,name) 12 this.age=age; 13 } 14 function inheritePrototype(superType, subType){ 15 var proto=object(superType.prototype); 16 proto.constructor=subType; 17 subType.prototype=proto 18 } 19 inheritePrototype(AA,BB); 20 BB.prototype.sayAge=function(){ 21 console.log(this.age); 22 } 23 var instace_3=new BB("Bill",20); 24 instace_3.sayAge();//20
["Emily", "Jing", "Van", "Mike"]
浙公网安备 33010602011771号