Javascript高级程序设计_7_继承

Posted on 2018-01-29 12:26  Jonathan_C  阅读(110)  评论(0)    收藏  举报
  • 通过原型链来体现继承的思想,如下面两个对象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"]