代码改变世界

javascript object oriented programming (二)

2011-06-19 12:31  jalen  阅读(269)  评论(0编辑  收藏  举报
  /*
	* 如何生成一个"继承"多个对象的实例。
	* 也就是说一个实例对象继承了多个对象的属性
	* 
	*	
  */
	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