JavaScript命名空间模式实例详解


Posted in Javascript onJune 20, 2019

本文实例讲述了JavaScript命名空间模式。分享给大家供大家参考,具体如下:

前言

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

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

命名冲突

代码的脆弱性

难以测试

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

1.单一全局变量

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

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

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

示例如下:

var myApplication = (function() {
  function() {
    // ***
  },
  return {
    // **
  }
})();

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

2.命名空间前缀

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

var = myApplication_propertyA = {};
var = myApplication_propertyA = {};
function myApplication_myMethod() {
  // ***
}

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

3.对象字面量表示法

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

var myApplication = {
  // 可以很容易的为对象字面量定义功能
  getInfo:function() {
    // ***
  },
  // 可以进一步支撑对象命名空间
  models:{},
  views:{
    pages:{}
  },
  collections:{}
};

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

var myApplication = myApplication || {};
if(!myApplication) {
  myApplication = {};
}
window.myApplication || (window.myApplication || {});
// 针对jQuery
var myApplication = $.fn.myApplication = function() {};
var myApplication = myApplication === undefined ? {} :myApplication;

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

4.嵌套命名空间

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

var myApplication = myApplication || {};
// 定义嵌套子对象
myApplication.routers = myApplication.routers || {};
myApplication.routers.test = myApplication.routers.test || {};

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

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

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

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

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

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

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

6.命名空间注入

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

var myApplication = myApplication || {};
myApplication.utils = {};
(function () {
  var value = 5;
  this.getValue = function () {
    return value;
  }
  // 定义新的子命名空间
  this.tools = {};
}).apply(myApplication.utils);
(function () {
  this.diagnose = function () {
    return "diagnose";
  }
}).apply(myApplication.utils.tools);
// 同样的方式在普通的IIFE上扩展功能,仅仅将上下文作为参数传递并修改,而不是仅仅使用this

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

var ns = ns || {},
  ns1 = ns1 || {};
// 模块、命名空间创建者
var creator = function (val) {
  var val = val || 0;
  this.next = function () {
    return val ++ ;
  };
  this.reset = function () {
    val = 0;
  }
}
creator.call(ns);
// ns.next, ns.reset 此时已经存在
creator.call(ns1, 5000);
// ns1包含相同的方法,但值被重写为5000了

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

7.自动嵌套的命名空间

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

function extend(ns, nsStr) {
  var parts = nsStr.split("."),
    parent = ns,
    pl;
  pl = parts.length;
  for (var i = 0; i < pl; i++) {
    // 属性如果不存在,则创建它
    if (typeof parent[parts[i]] === "undefined") {
      parent[prats[i]] = {};
    }
    parent = parent[parts[i]];
  }
  return parent;
}
// 用法
var myApplication = myApplication || {};
var mod = extend(myApplication, "module.module2");

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

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
js中Math之random,round,ceil,floor的用法总结
Dec 26 Javascript
jquery实现checkbox 全选/全不选的通用写法
Feb 22 Javascript
jQuery中append()方法用法实例
Jan 08 Javascript
基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js详解
Mar 05 Javascript
JavaScript模块规范之AMD规范和CMD规范
Oct 27 Javascript
jquery easyUI中ajax异步校验用户名
Aug 19 Javascript
微信小程序 教程之条件渲染
Oct 18 Javascript
纯javascript版日历控件
Nov 24 Javascript
JS 在数组指定位置插入/删除数据的方法
Jan 12 Javascript
浅谈pc端rem字体设置的问题
Aug 03 Javascript
浅谈Node模块系统及其模式
Nov 17 Javascript
jQuery UI实现动画效果代码分享
Aug 19 jQuery
npm的lock机制解析
Jun 20 #Javascript
express如何解决ajax跨域访问session失效问题详解
Jun 20 #Javascript
JS去除字符串最后的逗号实例分析【四种方法】
Jun 20 #Javascript
如何在微信小程序中实现Mixins方案
Jun 20 #Javascript
js JSON.stringify()基础详解
Jun 19 #Javascript
使用jquery-easyui的布局layout写后台管理页面的代码详解
Jun 19 #jQuery
Vue-Cli 3.0 中配置高德地图的两种方式
Jun 19 #Javascript
You might like
DOTA2 探索永无止境 玩家自创强悍插眼攻略
2020/04/20 DOTA
PHP-CGI进程CPU 100% 与 file_get_contents 函数的关系分析
2011/08/15 PHP
PHP实现通过中文字符比率来判断垃圾评论的方法
2014/10/20 PHP
详解在YII2框架中使用UEditor编辑器发布文章
2018/11/02 PHP
PHP序列化的四种实现方法与横向对比
2018/11/29 PHP
PHP一个简单的无需刷新爬虫
2019/01/05 PHP
Javascript入门学习资料收集整理篇
2008/07/06 Javascript
判断客户端浏览器是否安装了Flash插件的多种方法
2010/08/11 Javascript
validator验证控件使用代码
2010/11/23 Javascript
jQuery的控件及事件(输入控件及回车事件)使用示例
2013/07/25 Javascript
动态添加option及createElement使用示例
2014/01/26 Javascript
深入理解JS中的变量及作用域、undefined与null
2014/03/04 Javascript
javascript数组遍历for与for in区别详解
2014/12/04 Javascript
js实现浏览本地文件并显示扩展名的方法
2015/08/17 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
nodejs进阶(6)—连接MySQL数据库示例
2017/01/07 NodeJs
解决webpack打包速度慢的解决办法汇总
2017/07/06 Javascript
Vue.js 中取得后台原生HTML字符串 原样显示问题的解决方法
2018/06/10 Javascript
Vuex 使用及简单实例(计数器)
2018/08/29 Javascript
vue 使某个组件不被 keep-alive 缓存的方法
2018/09/21 Javascript
jquery弹窗时禁止body滚动条滚动的例子
2019/09/21 jQuery
Python3实现的画图及加载图片动画效果示例
2018/01/19 Python
Python数据分析之双色球中蓝红球分析统计示例
2018/02/03 Python
简单了解python字符串前面加r,u的含义
2019/12/26 Python
英国时尚饰品和发饰购物网站:Claire’s
2017/07/04 全球购物
lookfantastic荷兰:在线购买奢华护肤、护发和化妆品
2018/11/27 全球购物
美国最大的在线生存商店:Survival Frog
2020/12/13 全球购物
物业门卫岗位职责
2013/12/28 职场文书
运动会闭幕式解说词
2014/02/21 职场文书
让生命充满爱演讲稿
2014/05/10 职场文书
文明社区申报材料
2014/08/21 职场文书
2014年政教处工作总结
2014/12/20 职场文书
出生公证书
2015/01/23 职场文书
通知书大全
2015/04/27 职场文书
校运会通讯稿
2015/07/18 职场文书
生日祝酒词大全
2015/08/10 职场文书