原帖:
最近在学习html5,玩了下canvas,发现js中很多的东西都不太记得了。翻了下笔记后发现还是去图书馆逛逛把,到借阅区找了我一直想看的《javascript design patterns》好好研读了个下午,读罢,顿时有种醍醐顿开的感觉(夸张了..),发现之前对javascript OO方面的认识真的很浅,读了前几章关于OO的介绍后感觉思路清晰很多了,对于js一些基本概念的认识也加深了很多。同时也感概到程序员的想象力之丰富,可以将js模仿如此想传统的OO语言。当然这也更适合我们这些用惯了后台服务器语言开发的人来使用js。
废话了一段,算是这个笔记的开始把,接下来就开始了。首先还是笔记下js如何模拟继承的。
(一)js中的继承分为2种,一种是类式继承,另一种是原型式继承。
1.关于原型链接
关于继承,不得不先讲下js的原型链,因为这是js实现继承的基础。
借用下《High Performance JavaScript》的一段代码和UML图,个人觉得这个是理解原型链比较好的例子。
1 function Book(title, publisher){
2 this.title = title; 3 this.publisher = publisher; 4 } 5 Book.prototype.sayTitle = function(){ 6 alert(this.title); 7 }; 8 var book1 = new Book("High Performance JavaScript", "Yahoo! Press"); 9 var book2 = new Book("JavaScript: The Good Parts", "Yahoo! Press");10 book1.sayTitle(); 11 book2.sayTitle();UML图如下:
代码很简单,从UML图我们可以看到,这正是理解原型链的关键,这里有2个名词要着重提到,一个是原型对象, 一个是prototype对象。在js中每个对象都有原型对象的,原型对象是其构造函数的prototype属性所指向的那个对象,这里又有个名词要注意,由于js中处处是对象(当然除了3种原始类型,不过在需要的情况下,他们也可以被包装成对象的),为了区别,我们把构造函数称为函数对象。并且只有函数对象才拥有prototype属性,这一点我们可以从UML图中看出。
从例子可以看到,每个实例化对象都有个_proto_这个内部属性,这个属性是指向其构造函数的prototype属性所指向的那个对象。这有什么用呢,我们看下这句代码:
1 Book.prototype.sayTitle = function(){
2 alert(this.title); 3 };但我们调用:
1 book1.sayTitle();
的时候,由于book1里面没有sayTitle()这个方法,那么怎么办呢?这个时候原型链就有用了,他会沿着book1的原型对象去找sayTitle()这个方法,找到了就调用。
理解了这一些,理解两种继承就很容易了。
2.先看下类式继承的例子:
1 //类式继承
2 //Person类 3 function Person(name){ 4 this.name = name; 5 6 } 7 Person.prototype.getName = function(){ 8 alert(this.name); 9 }10 11 //BOY类,继承Person类12 function Boy(name,age){ 13 Person.call(this,name);14 this.age = age; 15 }16 //将Person加到Boy的原型链中17 Boy.prototype = new Person();18 Boy.prototype.constructor = Boy;19 20 Boy.prototype.getAge = function(){ 21 alert(this.age);22 }23 24 var boy = new Boy("james","23");25 boy.getName();26 boy.getAge();用惯了java或C#等传统面向对象语言的人来看这些应该会有种亲切感把~,这种继承很符合我们关于传统继承的思想,先写个父类,再让子类来继承,可能16到18行的会有点不和谐对于不熟悉js的人来说,那么我们可以用一个extend方法把这个模拟继承的过程包装起来。
1 //extend函数,负责将superClass加到subClass的原型链上
2 function extend(subClass,superClass){ 3 function f(){};4 f.prototype = superClass.prototype;5 subClass.prototype = new f();6 subClass.prototype.constrcuctor = subClass;7 8 }这样就可以用
1 extend(Boy,Person);
代替掉16到18行,这样基本就和传统的很接近了。
还有一点要特别注意:就是prototype属性指向的是一个实例化了的对象
3.接下来介绍原型式继承
还是先看代码:
1 //原型式继承
2 //clone方法,主要是将object对象加到f的原型链上 3 function clone(object){ 4 function f(){}; 5 f.prototype = object; 6 return new f(); 7 8 } 9 10 //直接定义一个js对象11 var Person ={ 12 name : "james",13 getName : function(){ 14 alert(this.name);15 }16 17 }18 19 20 var boy = clone(Person);21 //还没改变前读取的是原来Person的默认值;22 boy.getName(); //输出James,即原来默认那个值23 boy.age = "23";24 boy.getAge = function(){ 25 alert(this.age);26 }27 28 boy.name = "kobe"; //改变了name的值29 boy.getName(); //输出kobe,说明把原来的值覆盖了30 boy.getAge();31 32 33 var girl = clone(Person);34 girl.getName(); //输出james,即默认值35 girl.name = "tracy"; //改变了name 36 girl.getName(); //输出tracy,说明把原来值覆盖了37从第10行看起,首先我们定义了一个json对象Person,注意我们没用function,就是说这是一个对象了而不是函数对象,然后我们用一个clone方法将其赋给了boy变量,由开头可以知道clone方法其实就是把Person对象加到空对象f的原型链上,然后返回f给boy,原型链已经创建成功即继承已完成。
但是,这种原型式继承有个比较特别的地方:读写的差异性。即刚开始的时候所有实例化对象都是指向同一份拷贝的,直到他们改变原来的值,这个从21到36行的实验我们可以看出他的差别。很直观,就不多说了。
4.下面讨论下两种继承的优劣处:
各有各的优缺点,这里我主要从习惯和性能两点来讨论。
从习惯来说,类式继承对于用惯了C#等OO语言的人来说真是太舒服了,可以用原来的习惯来开发前台代码,好处自然是大大的。从这点来说,原型式继承就相对来说比较晦涩一点点了,后台人员还要转变思维来习惯,同时对他的读写差异性上理解也容易产生一些偏差。
从性能来说,原型继承就会好一点,因为实例化对象的时候,在没改变值之前他们都是共享同一份拷贝的,这样可以节约内存开销。
所以,选择哪一种还是要根据具体的项目需求来决定。
ps:第一篇笔记终于完成,写了好久,呵呵~,写的过程基本没怎么翻书,都是凭借自己的理解来写的。原来想把知道的东西表达清楚也是要花下心思的...当然难免有什么理解不当,希望各位指出~