Appearance
JavaScript 中的单例模式
前面提到的几种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从“类”中创 建而来。在以类为中心的语言中,这是很自然的做法。比如在 Java 中,如果需要某个对象,就必 须先定义一个类,对象总是从类中创建而来的。但 JavaScript 其实是一门无类(class-free) 语言,也正因为如此,生搬单例模式的概念并无意义。在 JavaScript 中创建对象的方法非常简单, 既然我们只需要一个“唯一”的对象,为什么要为它先创建一个“类”呢?这无异于穿棉衣洗澡,传统 的单例模式实现在 JavaScript 中并不适用。单例模式的核心是确保只有一个实例,并提供全局访 问。
全局变量不是单例模式,但在 JavaScript 开发中,我们经常会把全局变量当成单例来使用。
javascript
var a = {}对象 a 确实是独一无二的。如果 a 变量被声明在全局作用域下,则我们可以在代码中的任何位置 使用这个变量,全局变量提供给全局访问是理所当然的。这样就满足了单例模式的两个条件。 但是全局变量存在很多问题,它很容易造成命名空间污染。在大中型项目中,如果不加以限制和 管理,程序中可能存在很多这样的变量。JavaScript 中的变量也很容易被不小心覆盖,就像上面的 对象 var a = {};,随时有可能被别人覆盖。Douglas Crockford 多次把全局变量称为 JavaScript 中最糟糕的特性。在对 JavaScript 的创造者 Brendan Eich 的访谈中, Brendan Eich 本人也承认全局变量是设计上的失误,是在没有足够的时间思考一些东西的情况下 导致的结果。作为普通的开发者,我们有必要尽量减少全局变量的使用,即使需要,也要把它的污染 降到最低。以下几种方式可以相对降低全局变量带来的命名污染。
使用命名空间
适当使用命名空间,并不会杜绝全局变量,但可以减少全局变量的数量。
javascriptvar namespace1 = { foo: function () {}, bar: function () {} }动态创建命名空间
javascriptvar myApp = {} myApp.namespace = function (name) { var parts = name.split('.') var current = this for (var i = 0, l = parts.length; i < l; i++) { var part = parts[i] if (!current[part]) { current[part] = {} } current = current[part] } } myApp.namespace('foo') myApp.namespace('bar.baz') // 上述代码等价于 var myApp = { foo: {}, bar: { baz: {} } }使用闭包封装私有变量
这种方法把一些变量封装在闭包的内部,只暴露一些接口跟外界通信
javascriptvar user = (function () { var _firstName = 'foo', _lastName = 'bar' return { getUserInfo: function () { return _firstName + '-' + _lastName } } })()外部访问不到 _firstName 和 _lastName 这两个变量,避免了全局变量的污染。