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 相关文章推荐
ajax上传时参数提交不更新等相关问题
Dec 11 Javascript
页面加载完成后再执行JS的jquery写法以及区别说明
Feb 22 Javascript
jQuery封装的tab选项卡插件分享
Jun 16 Javascript
浅谈javascript基础之客户端事件驱动
Jun 10 Javascript
详解JavaScript对象类型
Jun 16 Javascript
JavaScript 判断一个对象{}是否为空对象的简单方法
Oct 09 Javascript
详解vue静态资源打包中的坑与解决方案
Feb 05 Javascript
JS实现二维数组横纵列转置的方法
Apr 17 Javascript
JavaScript解析机制与闭包原理实例详解
Mar 08 Javascript
vue实现局部刷新的实现示例
Apr 16 Javascript
javascript面向对象三大特征之封装实例详解
Jul 24 Javascript
开发一个封装iframe的vue组件
Mar 29 Vue.js
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
PHP的FTP学习(一)
2006/10/09 PHP
从C/C++迁移到PHP——判断字符类型的函数
2006/10/09 PHP
php面向对象与面向过程两种方法给图片添加文字水印
2015/08/26 PHP
PHP加密技术的简单实现
2016/09/04 PHP
LaravelS通过Swoole加速Laravel/Lumen详解
2018/03/02 PHP
PHP使用HTML5 FileApi实现Ajax上传文件功能示例
2019/07/01 PHP
php多进程并发编程防止出现僵尸进程的方法分析
2020/02/28 PHP
javascript Firefox与IE 替换节点的方法
2010/02/24 Javascript
通过下拉框的值来确定输入框是否可以为空的代码
2011/10/18 Javascript
Javascript引用指针使用介绍
2012/11/07 Javascript
javascript替换已有元素replaceChild()使用介绍
2014/04/03 Javascript
JQuery教学之性能优化
2014/05/14 Javascript
jQuery实现的Div窗口震动特效
2014/06/09 Javascript
javascript实现点击后变换按钮显示文字的方法
2015/05/13 Javascript
浅谈Node.js中的定时器
2015/06/18 Javascript
理解javascript模块化
2016/03/28 Javascript
javascript正则表达式之分组概念与用法实例
2016/06/16 Javascript
Javascript打印局部页面实例
2016/06/21 Javascript
node.js爬虫爬取拉勾网职位信息
2017/03/14 Javascript
JS图片轮播与索引变色功能实例详解
2017/07/06 Javascript
vue.js项目中实用的小技巧汇总
2017/11/29 Javascript
浅谈在Vue.js中如何实现时间转换指令
2019/01/06 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
微信小程序如何实现精确的日期时间选择器
2020/01/21 Javascript
微信小程序实现简单购物车功能
2020/12/30 Javascript
python中的字典详细介绍
2014/09/18 Python
tensorflow学习笔记之简单的神经网络训练和测试
2018/04/15 Python
详解python-图像处理(映射变换)
2019/03/22 Python
使用canvas一步步实现图片打码功能的方法
2019/06/17 HTML / CSS
HTML5语义化元素你真的用对了吗
2019/08/22 HTML / CSS
什么是触发器(trigger)? 触发器有什么作用?
2013/09/18 面试题
介绍信模板
2015/01/31 职场文书
民间借贷借条范本
2015/05/25 职场文书
如何理解Vue前后端数据交互与显示
2021/05/10 Vue.js
Mysql Online DDL的使用详解
2021/05/20 MySQL
Python实现查询剪贴板自动匹配信息的思路详解
2021/07/09 Python