龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > Javascript编程 >

JavaScript重构深入剖析(1)(3)

时间:2013-03-06 14:58来源:未知 作者:admin 点击:
分享到:
利用原型和闭包,完成组件方法 终于要定义一个组件方法了,利用原型来实现。看看这样如何: function Player(name){ Player.MIN_EXTENDED_TIME=1; Player.MAX_EXTENDED_T

利用原型和闭包,完成组件方法

终于要定义一个组件方法了,利用原型来实现。看看这样如何:

  1. function Player(name){   
  2.     Player.MIN_EXTENDED_TIME = 1;   
  3.     Player.MAX_EXTENDED_TIME = 3;   
  4.     this._name = name;   
  5. };   
  6. Player.prototype.setName = function(name){   
  7.     this._name = name;   
  8. };   
  9. Player.prototype.toString = function(){   
  10.     return "Player: " + this._name;   
  11. };  
  12.  
  13. var player = new Player("WindowsMediaPlayer");   
  14. alert(player.toString()); //输出WindowsMediaPlayer   
  15. player.setName("RealPlayer");   
  16. alert(player.toString()); //输出RealPlayer   
  17. alert(Player.MAX_EXTENDED_TIME); 

恩,有封装、有常量、也有复写了Object的toString方法,至于继承之类的事情,咱们后面再说,初看看还不错。可是这样的组件方法定义不够优雅,也不够直观,方法都是放在独立的位置定义的,并没有和最开始的组件方法放置在一起,如果能像Java那样定义岂不更好?

对了,可以用闭包来实现。试试看吧:

  1. function Player(name){   
  2.     Player.MIN_EXTENDED_TIME = 1;   
  3.     Player.MAX_EXTENDED_TIME = 3;   
  4.     this._name = name;   
  5.     this.setName = function(name){   
  6.         this._name = name;   
  7.     };   
  8.     this.toString = function(){   
  9.         return "Player: " + this._name;   
  10.     };   
  11. };  
  12.  
  13. var player = new Player("WindowsMediaPlayer");   
  14. alert(player.toString()); //输出WindowsMediaPlayer   
  15. player.setName("RealPlayer");   
  16. alert(player.toString()); //输出RealPlayer   
  17. alert(Player.MAX_EXTENDED_TIME); 

不像Groovy里面,闭包做了很大程度上的强化,包括新的语法的支持;JavaScript的闭包是很简单的闭包,它没有特殊的需要额外学习的语法,任意一个function,里面只要包含未绑定变量,这些变量是在function所属的上下文环境中定义的,那么,这个function就是闭包。顺便罗嗦一句,和闭包相反的,不正是不包含任何未绑定变量的函数式代码吗?

写是写好了,可是转念一想,Player应当只有一份,它是单例的,最好我也能像Java那样弄一个单例模式出来 :),可是事不遂愿,我没有办法在JavaScript做一个private的构造器,用这种思路去实现单例模式似乎不可行……

怎么办?

然而天无绝人之路,我控制不了你new一个Player的对象,我却可以控制你new出来的这个Player对象的属性和行为!当你需要使用你new出来的Player的对象的时候,你发现根本无法完成,或者它只是一个空壳!真正的东西还是要靠单例中经典的getInstance方法来获得:

  1. function Player(){   
  2.     throw new Error("Can not instantiate a Player object.");   
  3. }; //这只是个空壳  
  4.  
  5. (function(){ //这才是货真价实的东西   
  6.     Player.MIN_EXTENDED_TIME = 1;   
  7.     Player.MAX_EXTENDED_TIME = 3;   
  8.     Player._player = false;   
  9.     Player.getInstance = function(){   
  10.         if(!Player._player){   
  11.             alert("Init...");   
  12.             Player._player = {   
  13.                 _name : name,   
  14.                 setName : function(name){   
  15.                     this._name = name;   
  16.                 },   
  17.                 toString : function(name){   
  18.                     return "Player: " + this._name;   
  19.                 }   
  20.             };   
  21.         }   
  22.         return Player._player;   
  23.     };   
  24. })();  
  25.  
  26. //var player = new Player(); //new Player()会抛出异常   
  27. var player1 = Player.getInstance();   
  28. var player2 = Player.getInstance();   
  29. player2.setName("RealPlayer");   
  30. alert(player2.toString()); //输出RealPlayer 

好,真不错,单例模式在JavaScript下也成功实施了――你要胆敢new Player();就会抛出一个异常,这样什么也得不到,只有用getInstance方法得到的对象才是真真正正的Player对象。上面的代码整个执行的结果,只弹出了一次"Init..."的对话框,说明真正的“构造器逻辑”只调用了一次。

都做到这份上了,依然有小小的遗憾,Player的定义依然被拆分成了两部分,一部分定义空壳,一部分是一个匿名函数来定义Player的常量和getInstance方法。这两部分就不能合二为一么?

能。只需要用到一个小小的匿名函数,如果耐心从头看到这里,也一定能理解:

  1. var Player = (function(){   
  2.     Player = function(){ //这只是个空壳   
  3.         throw new Error("Can not instantiate a Player object.");   
  4.     };   
  5.     Player.MIN_EXTENDED_TIME = 1;   
  6.     Player.MAX_EXTENDED_TIME = 3;   
  7.     Player._player = false;   
  8.     Player.getInstance = function(){   
  9.         if(!Player._player){   
  10.             alert("Init...");   
  11.             Player._player = {   
  12.                 _name : name,   
  13.                 setName : function(name){   
  14.                     this._name = name;   
  15.                 },   
  16.                 toString : function(name){   
  17.                     return "Player: " + this._name;   
  18.                 }   
  19.             };   
  20.         }   
  21.         return Player._player;   
  22.     };   
  23.     return Player; //把修缮完工的Player这个组件方法返回   
  24. })();  
  25.  
  26. //var player = new Player(); //new Player()会抛出异常   
  27. var player1 = Player.getInstance();   
  28. var player2 = Player.getInstance();   
  29. player2.setName("RealPlayer");   
  30. alert(player2.toString()); //输出RealPlayer 

到此,终于如释重负,深入理解JavaScript面向对象,用好原型和闭包这两把锋利的武器,才能写出优秀的前端代码来。

精彩图集

赞助商链接