js原型继承实现
时间:2011-06-03 来源:liyatang
原型继承和之前谈的类式继承有截然不同的概念。等一下你就会领会到。
原型继承也是基于原型链的工作机制,原型继承的名称也是由此而来。
我们这样定义Person类
var Person = { name : "default name", getName ; function(){ return this.name; } }
//变量Person有name和getName()成员,我们也可以换种方式定义
Person['getName'] = function(){ return this.name; }
原型继承的使用的方法很简单。不像类式继承那样要new一个对象。而是Person本身就是一个对象。
Person.getName();
接下来定义一个Chinese类继承于Person类,这里用到了clone函数,然后使用方式也很简单
var Chinese = clone(Person); show(Chinese.getName());//show default name Chinese.name = "liyatang"; show(Chinese.getName());//show liyatang
Chinese也可以添加自己的成员
Chinese.sex = "Man"; Chinese.getSex = function(){ return this.sex; } show(Chinese.getSex());//show Man
原型继承就是这么简单,只需要一个clone函数。根据原型继承的思想依据是原型链,我们可以这样定义clone函数。
function clone(obj){ function F(){}; F.prototype = obj; //设置原型链 return new F; //返回一个新的对象 }
然而需要注意的是:对于从原型对象继承而来的成员,其读和写具有内在的不对等性。比如对于读取Chinese.name的时候,如果在没有设置Chinese.name属性的前,得到的值是Person.name。对于写Chinese.name的时候,写的却是Chinese.name属性。就是说读和写分别对不同的对象进行了操作。
这和内存的分配有关。当我们clone(Person)得到一个对象赋值给Chinese对象时,实际上一个克隆并非其原型对象的一份完全独立的副本,而只是一个以那个对象为原型的空对象而已。var Chinese = clone(Person); 后,Chinese是一个空对象,其属性prototype指向Person原型。所以在读取name属性时是读取Person.name。而在设置name时,实际上是个Chinese添加了一个name属性。我们可以通过hasOwnProperty()来查看成员是否在本对象中。(hasOwnProperty不会查找原型链,只在本对象中查找)
从上面的代码中体现不出应该注意读和写不对称性这个特征。修改下代码如下。
var Person = { name : "default name", books : ['first','second'], getName : function(){ return this.name; } } var Chinese = clone(Person); Chinese.books.push('new book'); show(Chinese.books);//show first,second,new book Chinese.books = [];//需要重新弄多个books数组 Chinese.books.push('other book'); show(Chinese.books);//show other book
这里我们需要注意的是Chinese.books = [];多了一个步骤,而且必须做这个步骤,否者就是对Person.books操作。
跟之前的Chinese.name = ‘liyatang’直接设置麻烦的多。而且常常会稍不留神忽略做这样的工作。
对于这种类似的问题我们可以在父类中用工厂方法来实现特许成员的创建。
Person.createChildObject = function(){ return []; } Chinese.books = Person.createChildObject(); Chinese.books.push('otherbook'); show(Chinese.books);//show other book
原型继承和类式继承的对比。
高级的js程序员更喜欢用原型继承,而且总有一天需要懂得原型继承的工作机制(摘自书中)。现在还不是很明白为什么。
1、原型继承更能节约内存。原型链读取成员的方式使得所有克隆出来的对象都共享每个属性和方法的唯一一份实例,只有在直接设置了某个克隆出来的对象的属性和方法是,情况才会有所变化。与此相比,类式继承中创建的每一个对象在内存中都有自己的一套属性的副本。
这个可以通过hasOwnProperty()方法来检测。假设父类有name属性,用类式继承的话子类hasOwnProperty(‘name’) == true,用原型继承的话子类hasOwnProperty(‘name’) == flase。
2、原型继承更简约。它只用到一个clone函数,不用像类式继承好几行难懂的代码。不要把原型继承的简洁看作简陋,它的力量蕴藏在其简洁之中(摘自书中)。由于实战的经验少,上一句话我自己也认识不深。