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