javascript object oriented programming (二)
2011-06-19 12:31 jalen 阅读(271) 评论(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
浙公网安备 33010602011771号