javascript object oriented programming ,二

  /*
        * 如何生成一个"继承"多个对象的实例。
        * 也就是说一个实例对象继承了多个对象的属性
        * 
        *       
  */
        function Animal(){
                this.species = '动物';
        }

        function Cat(name,color){
                this.name = name;
                this.color = color;
        }
        //1. 构造函数绑定
        //1.最简单的方法,大概就是使用call或apply方法,将父对象的构造函数绑定在子对象上,也就是在子对象构造函数中加一行
        function Cat(name,color){
                Animal.apply(this,arguments);      //将Animal构造函数绑定在了Cat对象上
                this.name = name;
                this.color = color;
        }
        var cat1 = new Cat('大黄','yellow');
        console.log(cat1.species);                              //动物

        //2. prototype模式 更常见的做法,则是使用prototype属性。
        //如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。
                
        Cat.prototype = new Animal();    //Cat的prototype对象指向一个Animal的实例。
        Cat.prototype.constructor = Cat;
        var cat1 = new Cat('大黄','黄色');
        console.log(cat1.species);
        /*
        * 第二种方式的解释
        *任何一个prototype对象都有一个constructor属性,指向它的构造函数。
                也就是说,Cat.prototype 这个对象的constructor属性,是指向Cat的。
        *我们在前一步已经删除了这个prototype对象原来的值,所以新的prototype对象没有constructor属性,
                所以我们必须手动加上去,否则后面的"继承链"会出问题。这就是第二行的意思。
        * Cat.prototype = new Animal();         这里Cat.prototype对象的constructor的属性值,指向了Animal的实例,原本应该指向           Cat,也就是说已经删除了prototype对象constructor原来的值,所以新的prototype对象没有constructor属性(其实应该是有           constructor,但其值为Animal的实例对象,这是我的理解!),所以必须手动加上去,不然‘继承连’会出问题,
        * Cat.prototype.constructor = Cat;   //所以要把Cat.prototype.constructor的值指向原来的值
        */
        //总之,这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

      o.prototype = {}; //o的prototype的constructor设置为空了

        //那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

      o.prototype.constructor = o;
        
        //3. 直接继承prototype
        //由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),
        //直接继承Animal.prototype。
        function Animal(){
                
        }

        function Cat(name,color){
                this.name = name;
                this.color = color;
        }
        Animal.prototype.species = '动物1';

        //将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。
        Cat.prototype = Animal.prototype;
        //这一句实际上把Animal.prototype对象的constructor属性也改掉了!
        Cat.prototype.constructor = Cat;
                //console.log(Animal.prototype.constructor);   //Cat
        //console.log(Cat.prototype.constructor);                //Cat
        var cat1 = new Cat("大黄","yellow");
        console.log(cat1.species);
        /*
                与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。
        */
         
         //4. 利用空对象作为中介 (修复了上第3点方法直接继承的问题)
        //由于"直接继承prototype"存在上述的缺点,所以可以利用一个空对象作为中介。
        function Animal(){      
        }
        function Cat(name,color){
                
                this.name = name;
                this.color = color;
        }
        Animal.prototype.species = '动物';

        function F(){};
        //F是空对象,所以几乎不占内存。
        F.prototype = Animal.prototype;         
        Cat.prototype = new F();
        //这时,修改Cat的prototype对象的constructor属性,就不会影响到Animal的prototype对象的constructor属性。
        Cat.prototype.constructor = Cat;
        var cat1 = new Cat('大白','white');

    console.log(cat1.species);
        console.log(Cat.prototype.constructor);      //Cat  
        console.log(Animal.prototype.constructor);       //Animal
        
        //5. prototype模式的封装函数
        //我们将上面的方法,封装成一个函数extend,便于使用。

        function Animal(){      
        }
        function Cat(name,color){
                
                this.name = name;
                this.color = color;
        }
        Animal.prototype.species = '动物';
        //这个extend函数,就是YUI库如何实现继承的方法。
        function extend(Child,Parent){
                var F= function(){};
                F.prototype = Parent.prototype;
                Child.prototype = new F();
                Child.prototype.constructor = Child;
        //意思是为子对象设一个spare属性,这个属性直接指向父对象的prototype属性。这等于是在子对象上打开一条通道,
        //可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
                Child.spare = Parent.prototype;
        }

        extend(Cat,Animal);
        var cat1 = new Cat("大黄","黄色");
        console.log(cat1.species)

        //6. 拷贝继承
        //上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?
        //首先,还是把Animal的所有不变属性,都放到它的prototype对象上。
        //我的理解就是把,如果拷贝的只是prototype对象的属性,就是把父对象原型上的属性拷贝到子对象的原型上。
        function copy_extend(Child,Parent){
                var c = Child.prototype;
                var p = Parent.prototype;
                for(var i in p){
                        c[i] = p[i];
                }
                c.spare = p;
        }

        //consider following code
        function Animal(){      
        }
        function Cat(name,color){
                
                this.name = name;
                this.color = color;
        }
        Animal.prototype.species = '动物';
        Animal.prototype.method = '拷贝后的方法';
                function copy_extend(Child,Parent){
                        var c = Child.prototype;
                        var p = Parent.prototype;
                        for(var i in p){
                                c[i] = p[i];
                        }
                        c.spare = p;
                }

        copy_extend(Cat,Animal);
        var cat1 = new Cat("大黄","黄色");
        console.log(cat1.species);      //动物
        console.log(cat1.method);       //拷贝后的方法
        //个人认为,拷贝会增加memory的消耗。不如直接引用来的干脆。

/*

*以上6种实现继承的方法的总结。

* Notice:1.每个构造函数内都有一个prototype属性,而这个prototype属性指向prototype对象,在mozilla里叫做__proto__

2.没个prototype对象都有constructor属性,为了保持原来的原型链,每次改变值是要重新设置下。

*构造函数:其实就是一个普通函数,但是内部使用了this变量。

对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上

* <1 直接用call,apply引用方法,最简单。第一个参数是context上下文,第二参数是给引用方法的传入的参数。

* <2 prototype模式,Animal函数内存在的方法,然后通过设置Animal的实例对象Cat的prototype.此时Cat上就有了Animal的

属性了

* <3 直接继承prototype,Cat.prototype = Animal.prototype, 然后实例化对象cat1就可以用Animal原型上的方法了。

前提是Animal的方法在他的prototype对象上,但是Cat实例对象无法使用他构造函数内的属性和方法,除非在

Cat.prototype = new Animal(); 这样就引用了Animal函数内的属性和方法了,以及其prototype上的属性。

这个方法的弊端就是相互引用后,改期任意一方的constructor属性,其他的一个也会改变。

<4 利用空对象作为中介,可以弥补3的弊端。利用空对象F的prototype对象来引用Animal的prototype。然后将

Cat.prototype = new F() 这样就可以使用Animal函数的方法了,然后重新设置下constructor属性.

Cat.prototype.constructor = Cat Animal的prototype对象的constructor属性不会受影响。

<5 将第4种方法封装为函数,之外,把子对象的属性引用了父对象的prototype对象 Chlid.spare = Parent.prototype

<6 无非是把父对象中的所有属性和方法(包括父对象prototype上的所有属性)都拷贝到了子对象里,这样子对象也就可以使 用了。但这样完全没必要,耗memory!直接引用最方便。

*/

学习笔记,记录学习的过程。

原文链接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html