`

JavaScript重构(五):利用原型和闭包,完成组件方法

阅读更多
var Player = (function(){ 
  Player = function(){ //这只是个空壳 
  throw new Error("Can not instantiate a Player object."); 
}; 
Player.MIN_EXTENDED_TIME = 1; 
Player.MAX_EXTENDED_TIME = 3; 
Player._player = false; 
Player.getInstance = function(){ 
  if(!Player._player){ 
    alert("Init..."); 
    Player._player = { 
      _name : name, 
      setName : function(name){ 
        this._name = name; 
      }, 
      toString : function(){ 
        return "Player: " + this._name; 
      } 
    }; 
  } 
  return Player._player; 
}; 
return Player; //把修缮完工的Player这个组件方法返回 
})(); 

//var player = new Player(); //new Player()会抛出异常 
var player1 = Player.getInstance(); 
var player2 = Player.getInstance(); 
player2.setName("RealPlayer"); 
alert(player2.toString()); //输出RealPlayer

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

function Player(name){ 
  Player.MIN_EXTENDED_TIME = 1; 
  Player.MAX_EXTENDED_TIME = 3; 
  this._name = name; 
}; 
Player.prototype.setName = function(name){ 
  this._name = name; 
}; 
Player.prototype.toString = function(){ 
  return "Player: " + this._name; 
}; 

var player = new Player("WindowsMediaPlayer"); 
alert(player.toString()); //输出WindowsMediaPlayer 
player.setName("RealPlayer"); 
alert(player.toString()); //输出RealPlayer 
alert(Player.MAX_EXTENDED_TIME);

 

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

 

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

function Player(name){ 
  Player.MIN_EXTENDED_TIME = 1; 
  Player.MAX_EXTENDED_TIME = 3; 
  this._name = name; 
  this.setName = function(name){ 
    this._name = name; 
  }; 
  this.toString = function(){ 
    return "Player: " + this._name; 
  }; 
}; 

var player = new Player("WindowsMediaPlayer"); 
alert(player.toString()); //输出WindowsMediaPlayer 
player.setName("RealPlayer"); 
alert(player.toString()); //输出RealPlayer 
alert(Player.MAX_EXTENDED_TIME);

 

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

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

怎么办?

 

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

function Player(){ 
  throw new Error("Can not instantiate a Player object."); 
}; //这只是个空壳 

(function(){ //这才是货真价实的东西 
  Player.MIN_EXTENDED_TIME = 1; 
  Player.MAX_EXTENDED_TIME = 3; 
  Player._player = false; 
  Player.getInstance = function(){ 
    if(!Player._player){ 
      alert("Init..."); 
      Player._player = { 
        _name : name, 
        setName : function(name){ 
          this._name = name; 
        }, 
        toString : function(name){ 
          return "Player: " + this._name; 
        } 
      }; 
    } 
    return Player._player; 
  }; 
})(); 

//var player = new Player(); //new Player()会抛出异常 
var player1 = Player.getInstance(); 
var player2 = Player.getInstance(); 
player2.setName("RealPlayer"); 
alert(player2.toString()); //输出RealPlayer

 

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

 

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

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

 

var Player = (function(){ 
    Player = function(){ //这只是个空壳 
        throw new Error("Can not instantiate a Player object."); 
    }; 
    Player.MIN_EXTENDED_TIME = 1; 
    Player.MAX_EXTENDED_TIME = 3; 
    Player._player = false; 
    Player.getInstance = function(){ 
        if(!Player._player){ 
            alert("Init..."); 
            Player._player = { 
                _name : name, 
                setName : function(name){ 
                    this._name = name; 
                }, 
                toString : function(name){ 
                    return "Player: " + this._name; 
                } 
            }; 
        } 
        return Player._player; 
    }; 
    return Player; //把修缮完工的Player这个组件方法返回 
})();

//var player = new Player(); //new Player()会抛出异常 
var player1 = Player.getInstance(); 
var player2 = Player.getInstance(); 
player2.setName("RealPlayer"); 
alert(player2.toString()); //输出RealPlayer

 

到此,终于如释重负,深入理解JavaScript面向对象,用好原型和闭包这两把锋利的武器,才能写出优秀的前端代码来。有一些同事私下和我交流,后面我尽量贴简洁的代码,希望有面向对象基础和JavaScript基础的同事都能有所收获。

 

文章系本人原创,转载请注明作者和出处

分享到:
评论
2 楼 RayChase 2012-02-22  
踏月流星 写道
toString : function(name){  
        return "Player: " + this._name;  
      }
这个方法是不应该有参数的吧。

  已修正。
1 楼 踏月流星 2012-02-22  
toString : function(name){  
        return "Player: " + this._name;  
      }
这个方法是不应该有参数的吧。

相关推荐

    JavaScript内核高级教程

    - **基础概念**:介绍了JavaScript如何实现面向对象编程的基本原理,包括构造函数、原型链、继承机制等。 - **实战应用**:讲解如何利用这些概念来构建可扩展和易于维护的代码。 - **函数式JavaScript**: - **...

    seajs培训 重构代码

    - **闭包**:利用闭包可以创建私有变量和方法,避免全局污染。 - **原型链**:通过原型链实现对象间的继承,一个对象可以继承另一个对象的属性和方法。 - **动态性**:JavaScript的动态类型特性使得在运行时可以改变...

    javascript权威指南第五版(英文原版)

    2. **高级特性**:闭包、原型链、作用域、this关键字、函数表达式、箭头函数、模块化(CommonJS、ES6模块)、Promise、异步编程等。 3. **DOM操作**:通过JavaScript与HTML文档对象模型进行交互,包括元素选择、...

    JavaScript实战 源代码

    通过阅读和分析这些源代码,你可以深入理解JavaScript的基本语法、数据类型、控制结构、函数、对象、闭包、原型链等核心概念。同时,源代码还可能涵盖了DOM操作、事件处理、AJAX异步通信、ES6及以上的语法特性,如...

    JavaScript编程精解

    ### JavaScript编程精解知识点概述 #### 一、JavaScript简介 **JavaScript**是一种广泛...通过以上知识点的学习和实践,开发者可以更加深入地理解 JavaScript,并能够利用这些知识解决实际问题,提高开发效率和质量。

    深入浅出javascript源码

    4. **原型和原型链**:JavaScript的继承机制基于原型,通过原型对象实现属性和方法的共享。原型链是查找对象属性的路径,理解这一机制有助于掌握对象间的关联。 5. **事件和事件处理**:JavaScript与DOM交互的一个...

    javascript实例(1470个)

    JavaScript,简称JS,是一种轻量...每个实例都是一次实践的机会,通过阅读、理解和重构代码,你可以不断提升自己的编程技能,成为JavaScript的专家。同时,这些实例也可以作为开发过程中的参考资料,快速解决实际问题。

    高性能JavaScript + 高清+目录 PDF

    书中详细讨论了函数和对象的优化策略,包括函数表达式、立即执行函数、对象属性访问和优化、以及如何利用原型链来提高代码复用。此外,还特别关注了异步编程,如回调函数、Promise和async/await,这些都是现代...

    JavaScript+宝典(第四版)

    - 代码优化:学习编写高效、可维护的JavaScript代码,包括避免全局变量、使用let和const、代码重构等。 - 错误处理:掌握如何有效地捕获和处理运行时错误,以及使用try...catch语句。 - 调试技巧:使用浏览器...

    javascript-OOP:JavaScript OOP

    在JavaScript中,我们可以使用函数和闭包来实现封装,通过`this`关键字来引用对象的属性和方法。例如: ```javascript function Person(name) { this.name = name; this.sayHello = function() { console.log(`...

    Apress.Expert.JavaScript.Nov.2013

    - **对象与原型**:第一章“Objects and Prototyping”深入探讨了JavaScript中的对象和原型机制。这部分内容对于理解JavaScript的核心概念至关重要。通过学习这一章,读者可以掌握如何创建自定义对象,理解对象之间...

    javascript Function

    这段代码中,`Class`既是一个闭包,又是一个构造函数,同时还定义了一些私有变量和方法。 #### 七、处理Ajax返回的JS脚本 `Function`还可以用来处理Ajax请求返回的JavaScript脚本: ```javascript var ajax_js_...

    有趣的JavaScript玩具问题___下载.zip

    5. **对象和原型**:理解JavaScript中的对象和原型链,学习如何创建和操作对象,以及原型继承。 6. **函数式编程**:可能包含一些函数式编程的概念,如高阶函数、柯里化、纯函数等。 7. **异步编程**:了解回调...

    JavaScript 高清pdf 3件套

    这个部分重点介绍了原型和原型链的概念,这是理解JavaScript继承和对象创建机制的关键;闭包和作用域的理解,有助于编写更优雅的代码;"this"关键字的深入分析,将帮助开发者解决函数执行上下文的问题;模块化章节让...

    JavaScript帮助文档,JavaScript操作手册,JavaScript超强版

    《JavaScript超强版》可能是对JavaScript全面深入的解析,包括语言基础、数据类型、控制结构、函数、对象、原型链、闭包等核心概念,以及DOM操作、AJAX异步通信、BOM浏览器对象模型等相关知识。这本书可能还会涉及...

Global site tag (gtag.js) - Google Analytics