来源于:http://www.cnblogs.com/syfwhu/p/4885628.html

前言

命名空间可以被认为是唯一标识符下代码的逻辑分组。为什么会出现命名空间这一概念呢?因为可用的单词数太少,并且不同的人写的程序不可能所有的变量都没有重名现象。在JavaScript中,命名空间可以帮助我们防止与全局命名空间下的其他对象或变量产生冲突。命名空间也有助于组织代码,有更强的可维护性和可读性。本文旨在探讨JavaScript里的几种常见命名空间模式,为我们提供一个思路。

JavaScript执行环境有很多独特之处,全局变量和函数的使用就是其中之一。JavaScript的执行环境由各种各样的全局变量构成,这些全局变量先于函数执行环境而创建。这些全局变量都挂载于“全局对象”下,在浏览器中,window对象就等同于全局对象。那么,在全局作用域中声明的任何变量和函数都是window对象的属性,当名称有冲突时,就会产生一些不可控的问题。全局变量会带来以下问题:

  • 命名冲突
  • 代码的脆弱性
  • 难以测试

在编程开发中合理的使用命名空间,可以避免相同的变量或对象名称产生的冲突。而且,命名空间也有助于组织代码,有更强的可维护性和可读性。JavaScript中虽然没有提供原生的命名空间支持,但我们可以使用其他的方法(对象和闭包)实现类似的效果。下面就是一些常见的命名空间模式:

1.单一全局变量

JavaScript中一个流行的命名空间模式是选择一个全局变量作为主要的引用对象。因为每个可能的全局变量都成为唯一全局变量的属性,也就不用再创建多个全局变量,那么也就避免了和其他声明的冲突。

单一全局变量模式已经在不少的JavaScript类库中使用,如:

  • YUI定义了唯一的YUI全局对象
  • jQuery定义了$和jQuery,$由其他类库使用时使用jQuery
  • Dojo定义了一个Dojo全局变量
  • Closure类库定义了一个goog全局对象
  • Underscore类库定义了一个_ 全局对象

示例如下:

1 var myApplication = (function() {
2 function() {
3 // ***
4 },
5 return {
6 // **
7 }
8 })();

虽然单一全局变量模式适合某些情况,但其最大的挑战是确保单一全局变量在页面中是唯一使用的,不会发生命名冲突。

2.命名空间前缀

命名空间前缀模式其思路非常清晰,就是选择一个独特的命名空间,然后在其后面声明声明变量、方法和对象。示例如下:

1 var = myApplication_propertyA = {};
2 var = myApplication_propertyA = {};
3
4 function myApplication_myMethod() {
5 // ***
6 }

从某种程度上来说,它确实减少了命名冲突的发生概率,但其并没有减少全局变量的数目。当应用程序规模扩大时,就会产生很多的全局变量。在全局命名空间内,这种模式对其他人都没有使用的这个前缀有很强的依赖,而且有些时候也不好判断是否有人已经使用某个特殊前缀,在使用这种模式时一定要特别注意。

3.对象字面量表示法

对象字面量模式可以认为是包含一组键值对的对象,每一对键和值由冒号分隔,键也可以是代码新的命名空间。示例如下:

 1 var myApplication = {
2
3 // 可以很容易的为对象字面量定义功能
4 getInfo:function() {
5 // ***
6 },
7
8 // 可以进一步支撑对象命名空间
9 models:{},
10 views:{
11 pages:{}
12 },
13 collections:{}
14 };

与为对象添加属性一样,我们也可以直接将属性添加到命名空间。对象字面量方法不会污染全局命名空间,并在逻辑上协助组织代码和参数。并且,这种方式可读性和可维护性非常强,当然我们在使用时应当进行同名变量的存在性测试,以此来避免冲突。下面是一些常用的检测方法:

 1 var myApplication = myApplication || {};
2
3 if(!myApplication) {
4 myApplication = {};
5 }
6
7 window.myApplication || (window.myApplication || {});
8
9 // 针对jQuery
10 var myApplication = $.fn.myApplication = function() {};
11
12 var myApplication = myApplication === undefined ? {} :myApplication;

对象字面量为我们提供了优雅的键/值语法,我们可以非常便捷的组织代码,封装不同的逻辑或功能,而且可读性、可维护性、可扩展性极强。

4.嵌套命名空间

嵌套命名空间模式可以说是对象字面量模式的升级版,它也是一种有效的避免冲突模式,因为即使一个命名空间存在,它也不太可能拥有同样的嵌套子对象。示例如下:

1 var myApplication = myApplication || {};
2
3 // 定义嵌套子对象
4 myApplication.routers = myApplication.routers || {};
5 myApplication.routers.test = myApplication.routers.test || {};

当然,我们也可以选择声明新的嵌套命名空间或属性作为索引属性,如:

myApplication['routers'] = myApplication['routers'] || {};

使用嵌套命名空间模式,可以使代码易读且有组织性,而且相对安全,不易产生冲突。其弱点是,如果我们的命名空间嵌套过多,会增加浏览器的查询工作量,我们可以把要多次访问的子对象进行局部缓存,以此来减少查询时间。

5.立即调用的函数表达式

立即调用函数(IIFE)实际上就是匿名函数,被定义后立即被调用。在JavaScript中,由于变量和函数都是在这样一个只能在内部进行访问的上下文中被显式地定义,函数调用提供了一种实现私有变量和方法的便捷方式。IIFE是用于封装应用程序逻辑的常用方法,以保护它免受全局名称空间的影响,其在命名空间方面也可以发挥其特殊的作用。示例如下:

 1 // 命名空间和undefined作为参数传递,确保:
2 // 1.命名空间可以在局部修改,不重写函数外部上下文
3 // 2.undefined 的参数值是确保undefined,避免ES5规范里定义的undefined
4 (function (namespace, undefined) {
5
6 // 私有属性
7 var foo = "foo";
8 bar = "bar";
9
10 // 公有方法和属性
11 namespace.foobar = "foobar";
12 namespace.sayHello = function () {
13 say("Hello World!");
14 };
15
16 // 私有方法
17 function say(str) {
18 console.log("You said:" + str);
19 };
20 })(window.namespace = window.namespace || {});

可扩展性是任何可伸缩命名空间模式的关键,使用IIFE可以轻松实现这一目的,我们可以再次使用IIFE给命名空间添加更多的功能。

6.命名空间注入

命名空间注入是IIFE的另一个变体,从函数包装器内部为一个特定的命名空间“注入”方法和属性,使用this作为命名空间代理。这种模式的优点是可以将功能行为应用到多个对象或命名空间。示例如下:

 1 var myApplication = myApplication || {};
2 myApplication.utils = {};
3
4 (function () {
5 var value = 5;
6
7 this.getValue = function () {
8 return value;
9 }
10
11 // 定义新的子命名空间
12 this.tools = {};
13 }).apply(myApplication.utils);
14
15 (function () {
16 this.diagnose = function () {
17 return "diagnose";
18 }
19 }).apply(myApplication.utils.tools);
20
21 // 同样的方式在普通的IIFE上扩展功能,仅仅将上下文作为参数传递并修改,而不是仅仅使用this

还有一种使用API来实现上下文和参数自然分离的方法,该模式感觉更像是一个模块的创建者,但作为模块,它还提供了一个封装解决方案。示例如下:

 1 var ns = ns || {},
2 ns1 = ns1 || {};
3
4 // 模块、命名空间创建者
5 var creator = function (val) {
6 var val = val || 0;
7
8 this.next = function () {
9 return val ++ ;
10 };
11
12 this.reset = function () {
13 val = 0;
14 }
15 }
16
17 creator.call(ns);
18 // ns.next, ns.reset 此时已经存在
19
20 creator.call(ns1, 5000);
21 // ns1包含相同的方法,但值被重写为5000了

命名空间注入是用于为多个模块或命名空间指定一个类似的功能基本集,但最好是在声明私有变量或者方法时再使用它,其他时候使用嵌套命名空间已经足以满足需要了。

7.自动嵌套的命名空间

嵌套命名空间模式可以为代码单元提供有组织的结构层级,但每次创建一个层级时,我们也得确保其有相应的父层级。当层级数量很大时,会给我们带来很大的麻烦,我们不能快速便捷的创建想创建的层级。那么如何解决这个问题呢?Stoyan Stefanov提出,创建一个方法,其接收字符串参数作为一个嵌套,解析它,并自动用所需的对象填充基本名称空间。下面是这种模式的一种实现:

 1 function extend(ns, nsStr) {
2 var parts = nsStr.split("."),
3 parent = ns,
4 pl;
5
6 pl = parts.length;
7
8 for (var i = 0; i < pl; i++) {
9 // 属性如果不存在,则创建它
10 if (typeof parent[parts[i]] === "undefined") {
11 parent[prats[i]] = {};
12 }
13 parent = parent[parts[i]];
14 }
15 return parent;
16 }
17
18 // 用法
19 var myApplication = myApplication || {};
20 var mod = extend(myApplication, "module.module2");

以前我们必须为其命名空间将各种嵌套显式声明为对象,现在用上述更简洁、优雅的方式就实现了。

本文大致探讨了几种JavaScript中常用的命名空间模式。总的来说,没有大统一、通用的解决方案,只有适合的解决方案,具体选择哪种模式还要看项目需要。

数据冰冷的,但我们要让数据温暖起来,改变我们的生活!

JavaScript之命名空间模式 浅析的更多相关文章

  1. JavaScript之命名空间模式

    前言 命名空间可以被认为是唯一标识符下代码的逻辑分组.为什么会出现命名空间这一概念呢?因为可用的单词数太少,并且不同的人写的程序不可能所有的变量都没有重名现象.在JavaScript中,命名空间可以帮 ...

  2. JavaScript 实现命名空间(namespace)的最佳方案——兼容主流的定义类(class)的方法,兼容所有浏览器,支持用JSDuck生成文档

    作者: zyl910 一.缘由 在很多的面向对象编程语言中,我们可以使用命名空间(namespace)来组织代码,避免全局变量污染.命名冲突.遗憾的是,JavaScript中并不提供对命名空间的原生支 ...

  3. JS命名空间模式解析

    简介 在SF上看到这样一个提问: 如题,因为不得已的原因,需要写若干个全局函数.但又不想这样: window.a = function(){} window.b = function(){} wind ...

  4. javascript的对象创建模式---命名空间模式

    javascript中对象的概念是很普遍的,对象是是对象,数组是对象,函数也是对象,字符串其实也是对象.常见的对象创建方法有对象字面量.构造函数创建.我们先来看看对象的创建还有哪些更高级的模式. 一. ...

  5. javascript订阅模式浅析和基础实例

    前言 最近在开发redux或者vux的时候,状态管理当中的createStore,以及我们在组件中调用的dispatch传递消息给状态管理中心,去处理一些操作的时候,有些类似我们常见到订阅模式 于是写 ...

  6. Javascript 命名空间模式

    命名空间是通过为项目或库创建一个全局对象,然后将所有功能添加到该全局变量中.通过减少程序中全局变量的数量,实现单全局变量,从而在具有大量函数.对象和其他变量的情况下不会造成全局污染,同时也避免了命名冲 ...

  7. 精读JavaScript模式(七),命名空间模式,私有成员与静态成员

    一.前言 惰性十足,这篇2月19号就开始写了,拖到了现在,就是不愿意花时间把看过的东西整理一下,其它的任何事都比写博客要有吸引力,我要反省自己. 从这篇开始,是关于JS对象创建模式的探讨,JS语言简单 ...

  8. JavaScript高级---组合模式设计

    一.设计模式 javascript里面给我们提供了很多种设计模式: 工厂.桥.组合.门面.适配器.装饰者.享元.代理.观察者.命令.责任链 在前面我们实现了工厂模式和桥模式 工厂模式 : 核心:为了生 ...

  9. JavaScript高级---桥模式设计

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

随机推荐

  1. GeoEvent使用问题及解决方法整理

    假如GeoEvent的部署环境是一个典型的WebGIS架构(Portal+GIS Server),往往会遇到一些问题,例如: 问题:发布的StreamService流服务无法查看. 原因:默认发布的S ...

  2. Android—ZXing二维码扫描遇到的问题

    最近工作中需要开发带有二维码扫描功能的软件(基于开源项目ZXing),遇到的问题记录一下,也希望给大家带来帮助. 1.首先因为扫描要开摄像机所以加权限是一定的,不然后面什么都不能进行 <uses ...

  3. RSA算法及其在iOS中的使用

    因为项目中需要传输用户密码,为了安全需要用RSA加密,所以就学习了下RSA加密在iOS中的应用. 关于RSA的历史及原理,下面的两篇文章讲的很清楚了:  http://www.ruanyifeng.c ...

  4. Android 更新UI的几种方式

    1.Activity的 runOnUiThread textView = (TextView) findViewById( R.id.tv ); new Thread(new Runnable() { ...

  5. Android项目实战(二十四):项目包成jar文件,并且将工程中引用的jar一起打入新的jar文件中

    前言: 关于.jar文件: 平时我们Android项目开发中经常会用到第三方的.jar文件. 其实.jar文件就是一个类似.zip文件的压缩包,里面包含了一些源代码,注意的是.jar不包含资源文件(r ...

  6. iOS Swift-简单值(The Swift Programming Language)

    iOS Swift-简单值(The Swift Programming Language) 常量的声明:let 在不指定类型的情况下声明的类型和所初始化的类型相同. //没有指定类型,但是初始化的值为 ...

  7. LeakCanary内存泄漏检测工具使用步骤

    LeakCanary内存检测工具使用步骤: 第一步,进入app目录下的build.gradle,在最下面找到dependencies{},里面添加如下三行语句: debugCompile 'com.s ...

  8. WebApi Post 后台无法获取参数的解决方案

    事件回放: 之前一段时间,公司里前端用的Angularjs 发送http请求也是用的ng的组件,后台是.Net的WebApi 前端 var data = { PArgs: { PageIndex: 0 ...

  9. J2EE基础之JavaBean

    J2EE基础之JavaBean 1.什么是JavaBean? JavaBean本质上来说就是一个Java类,它通过封装属性和方法成为具有独立功能.可重复使用的,并可以与其他控件通信的组件对象.通过在J ...

  10. [WPF系列]-Prism+EF

      源码:Prism5_Tutorial   参考文档 Data Validation in WPF √ WPF 4.5 – ASYNCHRONOUS VALIDATION Reusable asyn ...