几种JavaScript定义类和对象的方法

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20a%20%3D%20'global'%3B%0A%09(function%20()%20%7B%0A%09%20%20%20%20alert(a)%3B%0A%09%20%20%20%20var%20a%20%3D%20'local'%3B%0A%09%7D)()%3B%0A" wmode="transparent"> 收藏代码javascript

  1. var a = 'global';  java

  2. (function () {  app

  3.     alert(a);  函数

  4.     var a = 'local';  this

  5. })();  spa

 

你们第一眼看到这个例子以为输出结果是什么?'global'?仍是'local'?其实都不是,输出的是undefined,不用迷惑,个人题外话就是为了讲这个东西的。prototype

其实很简单,看一看JavaScript运行机制就会明白。咱们能够把这种现象看作"预声明"。可是若是稍微深究一下,会明白得更透彻。设计

这里其实涉及到对象属性绑定机制。由于全部JavaScript函数都是一个对象。在函数里声明的变量能够看作这个对象的"相似属性"。对象属性的绑定在语言里是有分"早绑定"和"晚绑定"之分的。对象

【早绑定】是指在实例化对象以前定义其属性和方法。解析程序时能够提早转换为机器代码。一般的强类型语言如C++,java等,都是早绑定机制的。而JavaScript不是强类型语言。它使用的是"晚绑定"机制。继承

【晚绑定】是指在程序运行前,无需检查对象类型,只要检查对象是否支持特性和方法便可。能够在绑定前对对象执行大量操做而不受任何惩罚。

上面代码出现的"预声明"现象,咱们大可用"晚绑定"机制来解释。在函数的做用域中,全部变量都是"晚绑定"的。 即声明是顶级的。因此上面的代码和下面的一致:

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20a%20%3D%20'global'%3B%0A%09(function%20()%20%7B%0A%09%20%20%20%20var%20a%3B%0A%09%20%20%20%20alert(a)%3B%0A%09%20%20%20%20a%20%3D%20'local'%3B%0A%09%7D)()%3B%0A" wmode="transparent"> 收藏代码

  1. var a = 'global';  

  2. (function () {  

  3.     var a;  

  4.     alert(a);  

  5.     a = 'local';  

  6. })();  

 
在alert(a)以前只对a做了声明而没有赋值。因此结果可想而知。

在JavaScript里,我所知道的几种定义类和对象的方式:

直接量方式

使用直接量构建对象是最基础的方式,但也有不少弊端。

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20Obj%20%3D%20new%20Object%3B%0A%09Obj.name%20%3D%20'sun'%3B%0A%09Obj.showName%20%3D%20function()%20%7B%0A%09%20%20%20%20alert('this.name')%3B%0A%09%7D%0A" wmode="transparent"> 收藏代码

  1. var Obj = new Object;  

  2. Obj.name = 'sun';  

  3. Obj.showName = function() {  

  4.     alert('this.name');  

  5. }  

 

咱们构建了一个对象Obj,它有一个属性name,一个方法showName。可是若是咱们要再构建一个相似的对象呢?难道还要再重复一遍?NO!,咱们能够用一个返回特定类型对象的工厂函数来实现。就像工厂同样,流水线的输出咱们要的特定类型结果。

工厂方式

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09function%20createObj(name)%20%7B%0A%09%20%20%20%20var%20tempObj%20%3D%20new%20Object%3B%0A%09%20%20%20%20tempObj.name%20%3D%20name%3B%0A%09%20%20%20%20tempObj.showName%20%3D%20function%20()%20%7B%0A%09%20%20%20%20%20%20%20%20alert(this.name)%3B%0A%09%20%20%20%20%7D%3B%0A%09%20%20%20%20return%20tempObj%3B%0A%09%7D%0A%09var%20obj1%20%3D%20createObj('obj_one')%3B%0A%09var%20obj2%20%3D%20createObj('obj_two')%3B%0A" wmode="transparent"> 收藏代码

  1. function createObj(name) {  

  2.     var tempObj = new Object;  

  3.     tempObj.name = name;  

  4.     tempObj.showName = function () {  

  5.         alert(this.name);  

  6.     };  

  7.     return tempObj;  

  8. }  

  9. var obj1 = createObj('obj_one');  

  10. var obj2 = createObj('obj_two');  

 

这种工厂函数不少人是不把他当作构建对象的一种形式的。一部分缘由是语义:即它并不像使用了运算符new来构建的那么正规。还有一个更大的原 因,是由于这个工厂每次产出一个对象都会建立一个新函数showName(),即每一个对象拥有不一样的版本,但实际上他们共享的是同一个函数。

有些人把showName在工厂函数外定义,而后经过属性指向该方法,能够避开这个问题:

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09function%20showName%20()%20%7B%0A%09%20%20%20%20alert(this.name)%3B%0A%09%7D%20%20%20%20%0A%09function%20createObj(name)%20%7B%0A%09%20%20%20%20var%20tempObj%20%3D%20new%20Object%3B%0A%09%20%20%20%20tempObj.name%20%3D%20name%3B%0A%09%20%20%20%20tempObj.showName%20%3D%20showName%3B%0A%09%20%20%20%20return%20tempObj%3B%0A%09%7D%0A%09var%20obj1%20%3D%20createObj('obj_one')%3B%0A%09var%20obj2%20%3D%20createObj('obj_two')%3B%0A" wmode="transparent"> 收藏代码

  1. function showName () {  

  2.     alert(this.name);  

  3. }      

  4. function createObj(name) {  

  5.     var tempObj = new Object;  

  6.     tempObj.name = name;  

  7.     tempObj.showName = showName;  

  8.     return tempObj;  

  9. }  

  10. var obj1 = createObj('obj_one');  

  11. var obj2 = createObj('obj_two');  

 

惋惜的是,这种方式让showName()这个函数看起来不像对象的一个方法。

构造函数方式

这种方式是为了解决上面工厂函数的第一个问题,即没有new运算符的问题。但是第二个问题它依然不能解决。咱们来看看。

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09function%20Obj(name)%20%7B%0A%09%20%20%20%20this.name%20%3D%20name%3B%0A%09%20%20%20%20this.showName%20%3D%20function%20()%20%7B%0A%09%20%20%20%20%20%20%20%20alert(this.name)%3B%0A%09%20%20%20%20%7D%0A%09%7D%0A%09var%20obj1%20%3D%20new%20Obj('obj_one')%3B%0A%09var%20obj2%20%3D%20new%20Obj('obj_two')%3B%0A" wmode="transparent"> 收藏代码

  1. function Obj(name) {  

  2.     this.name = name;  

  3.     this.showName = function () {  

  4.         alert(this.name);  

  5.     }  

  6. }  

  7. var obj1 = new Obj('obj_one');  

  8. var obj2 = new Obj('obj_two');  

 

它的好处是不用在构造函数内新建一个对象了,由于new运算符执行的时候会自动建立一个对象,而且只有经过this才能访问这个对象。因此 咱们能够直接经过this来对这个对象进行赋值。并且不用再return,由于this指向默认为构造函数的返回值。同时,用了new关键字来建立咱们想 要的对象是否是感受更"正式"了。惋惜,它仍然不能解决会重复生成方法函数的问题,这个状况和工厂函数同样。

原型方式

这种方式对比以上方式,有个很大的优点,就是它解决了方法函数会被生成屡次的问题。它利用了对象的prototype属性。咱们依赖原型能够重写对象实例。

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20Obj%20%3D%20function%20()%20%7B%7D%0A%09Obj.prototype.name%20%3D%20'me'%3B%0A%09Obj.prototype.showName%20%3D%20function%20()%20%7B%0A%09%20%20%20%20alert(this.name)%3B%0A%09%7D%0A%09var%20obj1%20%3D%20new%20Obj()%3B%0A%09var%20obj2%20%3D%20new%20Obj()%3B%0A" wmode="transparent"> 收藏代码

  1. var Obj = function () {}  

  2. Obj.prototype.name = 'me';  

  3. Obj.prototype.showName = function () {  

  4.     alert(this.name);  

  5. }  

  6. var obj1 = new Obj();  

  7. var obj2 = new Obj();  

 

咱们依赖原型对构造函数进行重写,不管是属性仍是方法都是经过原型引用的方式给新建的对象,所以都只会被建立一次。惋惜的是,这种方式存在两个致命的问题:

  1. 没办法在构建对象的时候就写入想要的属性,由于原型在构造函数做用域外边,没办法经过传递参数的方式在对象建立的时候就写入属性值。只能在对象建立完毕后对值进行重写。

  2. 致命问题在于当属性指向对象时,这个对象会被多个实例所共享。考虑下面的代码:

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20Obj%20%3D%20function%20()%20%7B%7D%0A%09Obj.prototype.name%20%3D%20'me'%3B%0A%09Obj.prototype.flag%20%3D%20new%20Array('A'%2C%20'B')%3B%0A%09Obj.prototype.showName%20%3D%20function%20()%20%7B%0A%09%20%20%20%20alert(this.name)%3B%0A%09%7D%0A%09var%20obj1%20%3D%20new%20Obj()%3B%0A%09var%20obj2%20%3D%20new%20Obj()%3B%0A%09%20%0A%09obj1.flag.push('C')%3B%0A%09%20%0A%09alert(obj1.flag)%3B%20%2F%2F%20A%2CB%2CC%0A%09alert(obj2.flag)%3B%20%2F%2FA%2CB%2CC%0A" wmode="transparent"> 收藏代码

  1. var Obj = function () {}  

  2. Obj.prototype.name = 'me';  

  3. Obj.prototype.flag = new Array('A''B');  

  4. Obj.prototype.showName = function () {  

  5.     alert(this.name);  

  6. }  

  7. var obj1 = new Obj();  

  8. var obj2 = new Obj();  

  9.    

  10. obj1.flag.push('C');  

  11.    

  12. alert(obj1.flag); // A,B,C  

  13. alert(obj2.flag); //A,B,C  

 

是的,当flag属性指向对象时,那么实例obj1和obj2都共享它,哪怕咱们仅仅改变了obj1的flag属性,可是它的改变在实例obj2中任然可见。面对这个问题,让咱们不得不想是否应该把【构造函数方式】和【原型方式】结合起来,让他们互补。。。

构造函数和原型混合方式

咱们让属性用构造函数方式建立,方法用原型方式建立便可:

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20Obj%20%3D%20function%20(name)%20%7B%0A%09%20%20%20%20this.name%20%3D%20name%3B%0A%09%20%20%20%20this.flag%20%3D%20new%20Array('A'%2C%20'B')%3B%0A%09%7D%0A%09Obj.prototype%20%3D%20%7B%0A%09%20%20%20%20showName%20%3A%20function%20()%20%7B%0A%09%20%20%20%20%20%20%20%20alert(this.name)%3B%0A%09%20%20%20%20%7D%0A%09%7D%0A%09var%20obj1%20%3D%20new%20Obj()%3B%0A%09var%20obj2%20%3D%20new%20Obj()%3B%0A%09%20%0A%09obj1.flag.push('C')%3B%0A%09%20%0A%09alert(obj1.flag)%3B%20%2F%2F%20A%2CB%2CC%0A%09alert(obj2.flag)%3B%20%2F%2FA%2CB%0A" wmode="transparent"> 收藏代码

  1. var Obj = function (name) {  

  2.     this.name = name;  

  3.     this.flag = new Array('A''B');  

  4. }  

  5. Obj.prototype = {  

  6.     showName : function () {  

  7.         alert(this.name);  

  8.     }  

  9. }  

  10. var obj1 = new Obj();  

  11. var obj2 = new Obj();  

  12.    

  13. obj1.flag.push('C');  

  14.    

  15. alert(obj1.flag); // A,B,C  

  16. alert(obj2.flag); //A,B  

 

这种方式有效地结合了原型和构造函数的优点,是目前用的最多,也是反作用最少的方式。

不过,有些追求完美的家伙还不知足,由于在视觉上还没达到他们的要求,由于经过原型来建立方法的过程在视觉上仍是会让人以为它不太像实例的 方法(尤为对于传统OOP语言的开发者来讲。)因此,咱们可让原型活动起来,让他也加入到构造函数里面去,好让这个构造函数在视觉上更为统一。而这一系 列的过程只需用一个判断便可完成。

Js代码 <EMBED height=15 type=application/x-shockwave-flash pluginspage=http://www.macromedia.com/go/getflashplayer width=14 src=http://wangjie2013.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf allowscriptaccess="always" quality="high" flashvars="clipboard=%09var%20Obj%20%3D%20function%20(name)%20%7B%0A%09%20%20%20%20this.name%20%3D%20name%3B%0A%09%20%20%20%20this.flag%20%3D%20new%20Array('A'%2C%20'B')%3B%0A%09%20%20%20%20if%20(typeof%20Obj._init%20%3D%3D%20'undefined')%20%7B%0A%09%20%20%20%20%20%20%20%20Obj.prototype%20%3D%20%7B%0A%09%20%20%20%20%20%20%20%20%20%20%20%20showName%20%3A%20function%20()%20%7B%0A%09%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alert(this.name)%3B%0A%09%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%09%20%20%20%20%20%20%20%20%7D%3B%0A%09%20%20%20%20%20%20%20%20Obj._init%20%3D%20true%3B%0A%09%20%20%20%20%7D%0A%09%7D%0A" wmode="transparent"> 收藏代码

  1. var Obj = function (name) {  

  2.     this.name = name;  

  3.     this.flag = new Array('A''B');  

  4.     if (typeof Obj._init == 'undefined') {  

  5.         Obj.prototype = {  

  6.             showName : function () {  

  7.                 alert(this.name);  

  8.             }  

  9.         };  

  10.         Obj._init = true;  

  11.     }  

  12. }  

 

如上,用_init做为一个标志来判断是否已经给原型建立了方法。若是是那么就再也不执行。这样其实在本质上是没有任何变化的,方法还是经过原型建立,惟一的区别在于这个构造函数看起来"江山统一"了。

可是这种动态原型的方式是有问题的,《JavaScript高级程序设计》里并无深究。建立第一个对象的时候会由于prototype在对象实例化以前没来的及建起来,是根本没法访问的。因此第一个对象是没法访问原型方法的。同时这种方式在子类继承中也会有问题。

相关文章
相关标签/搜索