JavaScript学习笔记(二十一) 对象创建模式-命名空间模式

在JavaScript中创建对象是非常简单的,你可以使用对象字面量(object literal)或者使用构造函数(方法)(constructor)。

在这章里,我们会超出这些,看到一些额外的对象创建模式。

JavaScript语言是简单而直接的,通常没有特别的语法实现你在其它语言中使用的功能,比如命名空间(namespaces)、模块(modules)、包(packages),私有属性(private properties)和静态变量(static members)。接下来,我们会去实现这些常见的模式,或者使用其他方式代替,或者比较二者的不同。

我们会看到命名空间(namespacing),依赖声明(dependency declaration),模块模式(module pattern)和沙箱模式(sandbox patterns),这些会帮助组织和构建你的程序代码并且减少潜在的全局变量。讨论的主题还包括private或者享有特权(privileged)的成员变量,static或者private static的成员,对象常量,链式编程,使用特殊的方法的构造函数。

命名空间模式(Namespace Pattern)

命名空间会帮助减少程序需要的全局变量的数量,同时也帮助我们防止命名冲突或者过多的命名前缀(加前缀为了防止命名冲突)。
JavaScript语法本身没有内置的命名空间语法,但这个功能是十分容易实现的。为了防止大量的函数,对象或者其它变量污染全局作用域,你可以为你的程序或类库创建一个(理想情况只有一个)全局对象。然后你可以给这个对象添加所有的功能性需求。
像下面这个例子:
// BEFORE: 5 globals
// Warning: antipattern
// constructors
function Parent() {}
function Child() {}
// a variable
var some_var = 1;
// some objects
var module1 = {};
module1.data = {a: 1, b: 2};
var module2 = {};
你可以重构这种类型的代码通过为你的程序创建一个全局对象,比如叫做:MYAPP,并且将你所有的函数和变量作为这个全局变量的属性:
// AFTER: 1 global
// global object
var MYAPP = {};
// constructors
MYAPP.Parent = function () {};
MYAPP.Child = function () {};
// a variable
MYAPP.some_var = 1;
// an object container
MYAPP.modules = {};
// nested objects
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {a: 1, b: 2};
MYAPP.modules.module2 = {};
对于全局命名空间对象的名字,你可以选择你的程序或者类库的名字,你的域名,或者公司的名字。通常程序猿都会遵循约定,全局变量名全大写,所以更容易被代码的读者发现。(但也要记住全部大写通常也被用来命名常量)
这种模式是给你的代码添加命名空间和防止命名冲突的好方法,不仅仅可以防止你自己代码中的命名冲突,也可以防止你的代码和第三方代码命名冲突,比如JavaScript类库或控件。
这种模式被极力推荐并且可以完美适用于很多任务,但它也有一些不足之处:
  • 需要更多的输入;每个变量和函数都要添加一个前缀,会增加需要下载的代码量。
  • 只有一个全局变量意味着任何地方的代码都可以修改这个全局变量,其它地方都会受到影响。
  • 很长的嵌套名称会降低属性查找速度。
后面说到沙箱模式会解决这些不足。

通用的命名空间函数(General Purpose Namespace Function)

随着程序复杂度的增加,有部分代码会被分开放到不同的文件中,然后有条件的被包含(included), 假设你的代码是第一个声明某个命名空间或者它的属性是不安全的;
有些你正在添加的属性可能已经存在了,并且你可能覆盖了它们。
因此,在添加一个属性或者创建一个命名空间之前,最好先检查一下它是否已经存在,就像下面这个例子一样:
// unsafe
var MYAPP = {};
// better
if (typeof MYAPP === "undefined") {
    var MYAPP = {};
}
// or shorter
var MYAPP = MYAPP || {};
你可以看到这些添加的检查会很快导致大量重复代码。比如:如何你想定义MYAPP.modules.module2,你讲不得不进行三次检查,每个你定义的属性或对象都需要一次;
这就是为什么你需要一个方便的可重用的函数来具体维护命名空间。让我们将这个函数叫做namespace() 并且向这样去使用:
// using a namespace function
MYAPP.namespace('MYAPP.modules.module2');
// equivalent to:
// var MYAPP = {
//     modules: {
//         module2: {}
//     }
// };
接下来的例子是命名空间函数的实现。这个实现是非破坏性的,意味着如何一个命名空间已经存在,那么它不会被重新创建:
var MYAPP = MYAPP || {};
MYAPP.namespace = function (ns_string) {
    var parts = ns_string.split('.'),
        parent = MYAPP,
        i;
    // strip redundant leading global
    if (parts[0] === "MYAPP") {
        parts = parts.slice(1);
    }
    for (i = 0; i < parts.length; i += 1) {
        // create a property if it doesn't exist
        if (typeof parent[parts[i]] === "undefined") {
            parent[parts[i]] = {};
        }
        parent = parent[parts[i]];
    }
    return parent;
};
这个实现可以这样使用:
// assign returned value to a local var
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true
// skip initial `MYAPP`
MYAPP.namespace('modules.module51');
// long namespace
MYAPP.namespace('once.upon.a.time.there.was.this.long.nested.property');







你可能感兴趣的:(JavaScript学习笔记(二十一) 对象创建模式-命名空间模式)