zoukankan      html  css  js  c++  java
  • [Object]继承(经典版)(三)继承理论总结

    作者:zccst


    更新:在JavaScript模式中,作者在原继承的几种模式基础上,还总结了一些模式:

    1,原型继承  Child.prototype = new Parent();
    对于原型继承,在ECMA5中定义了Object.create();方法。实现了无new继承。
    var child = Object.create(parent, {//parent是父类对象
      age:11
    });

    2,对象冒充  Parent.call(this, arg1, ...);
    3,原型继承和对象冒充结合
    4,父子共用父类原型。Child.prototype = Parent.prototype;
    5,道格拉斯方法,使用空函数empty(){}或F(){}。优化后
    var inhert = (function(){
      F(){};
      return function(Child, Parent){
        //圣杯版代码
      }
    }());
    优化后可以只创建一个F。

    并且讲述了klass,代码封装的一般结构。(重点1)



    6,通过复制实现继承。浅复制与深复制。

    7,方法借用与绑定。(重点2)
    有时候仅仅想借用一个函数,让该函数运行在自己的作用域内。

    在事件绑定和回调函数情况下,函数的this可能指向了window,并不是自己所需要的this指向,此时如果对改方法进行绑定bind,使调 用该方法时始终指向自己的this,就是这个章节探讨的问题。ECMASCRIPT5中增加了bind方法,5之前也可以自己模拟实现。jQuery中的 实现是proxy(function(){}, this);改变函数运行环境。


    2014-01-20
    关于this指代总是常学常新,而且在与人讨论的过程中越辩越明,经过讨论(不同人在不同角度的理解)今天自以为总算完全掌握了。
    复习,在JavaScript中一共有四种调用模式:
    方法调用模式(对象属性模式)、
    函数调用模式(就是普通的函数执行)、
    构造调用模式(应该叫实例上的方法模式更好)
    call和apply调用模式。
    详情参考:http://zccst.iteye.com/blog/1931773

    方法借用属于call和apply调用模式,而方法调用模式和构造调用模式比较好理解。
    下面的例子都是函数调用模式,也即都是普通的函数执行,所以this指向window。

    Js代码  收藏代码
    1. var num = 1;  
    2. var myObject= {   
    3.     num: 2,   
    4.     add: function(){   
    5.         this.num = 3;   
    6.         (function(){  
    7.             alert(this.num);  //underfined this是Window。如果在全局里定义了var num = 1;则此处打印1  
    8.             this.num = 4;   
    9.             alert(this.num);  //4   
    10.         })();   
    11.         alert(this.num)   //3   
    12.     }   
    13. }   
    14. myObject.add(); //此时的闭包是函数执行模式,this指向window  

    Js代码  收藏代码
    1. var scope = "global";  
    2. var one = {  
    3.     scope : "one",  
    4.     getScope:function(){  
    5.         return this.scope;//this指向one  
    6.     }  
    7. };  
    8. var two = {  
    9.     scope : "two",  
    10.     getScope:function(){  
    11.         this.scope = "two_inner";  
    12.         return function(){  
    13.             return this.scope;//this指向Window  
    14.         };  
    15.         /* 
    16.         var _this = this; 
    17.         return function(){ 
    18.             return _this.scope; 
    19.         };*/  
    20.     }  
    21. };  
    22. var fun = two.getScope();  
    23. console.log(fun());//global //如果使用注释的那一段,则打印two_inner  
    24.   
    25. //问题是为什么this会指向Window呢?因为函数执行,所以this指向了window。  

    Js代码  收藏代码
    1. var one = {  
    2.     name : "object",  
    3.     say:function(greet){  
    4.         console.log(greet + ", " + this.name);  
    5.     }  
    6. };  
    7. var two = {  
    8.     name : "another object"  
    9. };  
    10. var yetanother = {  
    11.     name : "yet another object",  
    12.     method:function(callback){  
    13.         callback("Hola");  
    14.     }  
    15. };  
    16. yetanother.method(one.say);//Hola, undefined  
    17. //原因?one.say是函数声明function(greet){console.log(greet+','+this.name);}  
    18. //该函数暴露在全局作用域中,callback("Hola");相当于是执行该函数,所以callback的作用域是window。根本原因是函数执行并没有绑定到任何地方。  





    相关的一个问题:
    Java代码  收藏代码
    1. var a = 100;    
    2. function test(){    
    3.     alert(a);  //undefined  
    4.     var a = 10;    
    5.     alert(a);  //10  
    6. }    
    7. test();    
    8. //原因是:预编译变量声明和函数声明    












    继承方式:

    一、原型链继承
    本质是用父类的对象替换子类原型对象,当然父类的实例属性和实例方法,变成了子类的原型对象中的属性和方法了,父类的原型属性和方法保持没变

    优点:解决了原型属性和方法的复用
    缺点:
    1,父类的实例属性和方法在成为子类的原型属性和方法。父类的实例属性和方法会被所有子类对象共享。
    2,不能向父类的实例属性和方法传参(也无法改变,只能被所有子类对象共用一份)
    批注:博主说每new一个子类对象,都会保存一份父类实例属性和方法,我认为是错误的。因为父类实例作为子类的原型对象,是一种指针关系,所以实际情况是父类的实例属性和方法会被所有子类对象共享。证明:通过一个实例修改了某一个属性的值,会通过另一个实例体现出来。
    3,如果层层继承,子类对象[[prototype]]属性指针指向的原型对象的constructor指针始终指向最祖先构造函数

    二、call继承
    Call继承的本质是函数调用,只不过是函数调用时的this域是子类对象,当然没法继承父类的原型方法了

    优点:解决了父类实例属性和方法的复用(父类的实例属性和方法,直接是子类的实例属性和方法)
    缺点:不继承父类的原型属性和方法


    三、同时使用call和原型
    Call解决了父类实例属性和方法的复用
    prototype解决了原型属性和方法的复用

    缺点:产生两份实例属性和方法。一份在子类对象中,另一份在子类对象的原型对象中,而且前者会覆盖后者。





    原型继承的其他实现方式
    1,直接将父类的prototype赋给子类的prototype
    缺点:父类与子类耦合太高,因为改动了子类的prototype会影响父类。即使将原型对象的constructor指向子类构造函数,但子类和父类的实例都会共有相同的constructor,这种情形下修改子类的共有方法,同时会修改了父类的共有方法,说明此法不通。


    2,niubility方式-利用空函数实现继承【寄生组合式继承】
    function Empty(){}
    Empty.prototype = Person.prototype;//将父类的prototype赋给空的prototype
    Student.prototype = new Empty();
    Student.prototype.constructor = Student;
    优点:这种情况下修改Student的prototype就不会影响到Person的prototype对象了,并且,因为直接将Person的 prototype赋给Empty的prototype,所以不会存在特权属性(实例属性)浪费资源的问题。这样利用空函数就能很好的解决共有方法的继承 问题了。当然这时Student.prototype中的constructor是Person,所以最好加上 Student.prototype.constructor = Student转换过来。

    刚刚看高程,又发现这种方式还有一个特别文雅的名字,寄生组合式继承。
    所谓寄生,就是用一个空对象做马甲,只把原型属性和方法继承过来。
    再用Call继承实例属性和方法,这样就完美了,解决了前面组合继承中的实例属性和方法有两份的冗余。



    3,利用循环遍历拷贝的方法实现继承(循环将父类原型对象赋给子类原型对象)
    for(var i in Person.prototype){
    Student.prototype[i] = Person.prototype[i];
    }
    Student.prototype.constructor = Student;
    优点:
    父类原型对象的constructor指向父类构造函数,
    子类原型对象的constructor指向子类构造函数,

    这种循环方式,跟父类的实例属性和方法,没有任何关系,所以继承的实现用Call。








    其他非主流继承
    1,对象冒充继承
    缺点:与Call一样,本质仍是函数调用,只不过是赋给子类的一个实例属性
  • 相关阅读:
    D. Constructing the Array
    B. Navigation System
    B. Dreamoon Likes Sequences
    A. Linova and Kingdom
    G. Special Permutation
    B. Xenia and Colorful Gems
    Firetrucks Are Red
    java getInstance()的使用
    java 静态代理和动态代理
    java 类加载机制和反射机制
  • 原文地址:https://www.cnblogs.com/shsgl/p/4289904.html
Copyright © 2011-2022 走看看