前言

昨天,公司同事问了我如下一个问题:

说他在看一个插件时,看到了源码结构如截图所示,他知道(function(){})()是一种立即执行函数,但是在截图中,最后的那个圆括号里又写了一个函数function($,ChineseDistricts){...},这个函数暂且称为“匿名函数1”,function (factory){...}暂且称为“”匿名函数2”,意思是不是:把匿名函数1传入到匿名函数2的参数factory中,然后检测当前环境。如果检测到了全局环境中存在exports对象,则证明是node环境,如果是node环境,则用factory(require('jquery'), require('ChineseDistricts'))这个方法来执行匿名函数1,因为Node是基于模块的,所以在Node中要使用这个插件的话,必须用require()方法把匿名函数2中需要的参数"$"和"ChineseDistricts"以模块的方式给引用进来?

看到截图的代码和它的疑问,请接着往下看......

一、兼容多种模块规范(AMD,CMD,Node)的代码

在JavaScript模块化开发中,为了让同一个模块可以运行在前后端,以及兼容多种模块规范(AMD,CMD,Node),类库开发者需要将类库代码包装在一个闭包内。

1.AMD规范

AMD,即“异步模块定义”。主要实现比如: RequireJS。

其模块引用方式如下:define(id?,dependencies?,factory);

其中,id及依赖是可选的。其与CommonJS方式相似的地方在于factory的内容就是实际代码的内容,下面是一个简单的例子:

define(function(){var exports = {};exports.say = function(){alert('hello');};return exports;
});

2.CMD规范

CMD规范,与AMD类似,区别主要在于定义模块和依赖引入的地方。主要实现比如: SeaJS。

主要区别是:AMD需要在声明模块时指定所有的依赖,通过形参传递依赖到模块内容中。

define(['dep1','dep2'],function(dep1,dep2){return function(){};
});

与AMD相比,CMD更接近Node对CommonJS规范的定义:

define(factory);

在依赖部分,CMD支持动态引入,如下:

define(function(require,exports,module){// Todo
});

require、exports、module通过形参传递给模块,在需要依赖模块时,随时调用require引入即可。

3.CommonJS的模块实现

CommonJS的模块引用使用require,如下:

var http = require('http');

4.Node的模块实现

在Node中引入模块,需要经过下面3个步骤:路径分析;文件定位;编译执行。主要用require()方法来查找模块。

5.兼容多种模块规范

为了让同一个模块可以运行在前后端,在开发过程中需要考虑兼容前后端问题,以下代码演示如何将hello()方法定义到不同的运行环境中,它能够兼容AMD、CMD、Node以及常见的浏览器环境中:

;(function (name, definition) {// 检测上下文环境是否为AMD或CMDvar hasDefine = typeof define === 'function',// 检查上下文环境是否为NodehasExports = typeof module !== 'undefined' && module.exports;if (hasDefine) {// AMD环境或CMD环境define(definition);} else if (hasExports) {// 定义为普通Node模块module.exports = definition();} else {// 将模块的执行结果挂在window变量中,在浏览器中this指向window对象this[name] = definition();}
})('hello', function () {var hello = function () {};return hello;
});

二、如何封装Node.js和前端通用的模块

在Node.js中对模块载入和执行进行了包装,使得模块文件中的变量在一个闭包中,不会污染全局变量,和他人冲突。

前端模块通常是我们开发人员为了避免和他人冲突才把模块代码放置在一个闭包中。

如何封装Node.js和前端通用的模块,我们可以参考Underscore.js 实现,他就是一个Node.js和前端通用的功能函数模块,查看代码:

// Create a safe reference to the Underscore object for use below.var _ = function(obj) {if (obj instanceof _) return obj;if (!(this instanceof _)) return new _(obj);this._wrapped = obj;};// Export the Underscore object for **Node.js**, with// backwards-compatibility for the old `require()` API. If we're in// the browser, add `_` as a global object via a string identifier,// for Closure Compiler "advanced" mode.if (typeof exports !== 'undefined') {if (typeof module !== 'undefined' && module.exports) {exports = module.exports = _;}exports._ = _;} else {root._ = _;}

通过判断exports是否存在来决定将局部变量 _ 赋值给exports,向后兼容旧的require() API,如果在浏览器中,通过一个字符串标识符“_”作为一个全局对象;完整的闭包如下:

(function() {// Baseline setup// --------------// Establish the root object, `window` in the browser, or `exports` on the server.var root = this;// Create a safe reference to the Underscore object for use below.var _ = function(obj) {if (obj instanceof _) return obj;if (!(this instanceof _)) return new _(obj);this._wrapped = obj;};// Export the Underscore object for **Node.js**, with// backwards-compatibility for the old `require()` API. If we're in// the browser, add `_` as a global object via a string identifier,// for Closure Compiler "advanced" mode.if (typeof exports !== 'undefined') {if (typeof module !== 'undefined' && module.exports) {exports = module.exports = _;}exports._ = _;} else {root._ = _;}
}).call(this);

通过function定义构建了一个闭包,call(this)是将function在this对象下调用,以避免内部变量污染到全局作用域。浏览器中,this指向的是全局对象(window对象),将“_”变量赋在全局对象上“root._”,以供外部调用。

和Underscore.js 类似的Lo-Dash,也是使用了类似的方案,只是兼容了AMD模块载入的兼容:

;(function() {/** Used as a safe reference for `undefined` in pre ES5 environments */var undefined;/** Used to determine if values are of the language type Object */var objectTypes = {'boolean': false,'function': true,'object': true,'number': false,'string': false,'undefined': false};/** Used as a reference to the global object */var root = (objectTypes[typeof window] && window) || this;/** Detect free variable `exports` */var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;/** Detect free variable `module` */var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;/** Detect the popular CommonJS extension `module.exports` */var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;/*--------------------------------------------------------------------------*/// expose Lo-Dashvar _ = runInContext();// some AMD build optimizers, like r.js, check for condition patterns like the following:if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {// Expose Lo-Dash to the global object even when an AMD loader is present in// case Lo-Dash was injected by a third-party script and not intended to be// loaded as a module. The global assignment can be reverted in the Lo-Dash// module by its `noConflict()` method.root._ = _;// define as an anonymous module so, through path mapping, it can be// referenced as the "underscore" moduledefine(function() {return _;});}// check for `exports` after `define` in case a build optimizer adds an `exports` objectelse if (freeExports && freeModule) {// in Node.js or RingoJSif (moduleExports) {(freeModule.exports = _)._ = _;}// in Narwhal or Rhino -requireelse {freeExports._ = _;}}else {// in a browser or Rhinoroot._ = _;}
}.call(this));

再来看看Moment.js的封装闭包主要代码:

(function (undefined) {var moment;// check for nodeJSvar hasModule = (typeof module !== 'undefined' && module.exports);
/************************************Exposing Moment************************************/function makeGlobal(deprecate) {var warned = false, local_moment = moment;/*global ender:false */if (typeof ender !== 'undefined') {return;}// here, `this` means `window` in the browser, or `global` on the server// add `moment` as a global object via a string identifier,// for Closure Compiler "advanced" modeif (deprecate) {this.moment = function () {if (!warned && console && console.warn) {warned = true;console.warn("Accessing Moment through the global scope is " +"deprecated, and will be removed in an upcoming " +"release.");}return local_moment.apply(null, arguments);};} else {this['moment'] = moment;}}// CommonJS module is definedif (hasModule) {module.exports = moment;makeGlobal(true);} else if (typeof define === "function" && define.amd) {define("moment", function (require, exports, module) {if (module.config().noGlobal !== true) {// If user provided noGlobal, he is aware of globalmakeGlobal(module.config().noGlobal === undefined);}return moment;});} else {makeGlobal();}
}).call(this);

从上面的几个例子可以看出,在封装Node.js和前端通用的模块时,可以使用以下逻辑:

if (typeof exports !== "undefined") {exports.** = **;
} else {this.** = **;
}

即,如果exports对象存在,则将局部变量装载在exports对象上,如果不存在,则装载在全局对象上。如果加上ADM规范的兼容性,那么多加一句判断:

if (typeof define === "function" && define.amd){}

参考链接:
1.http://www.css88.com/archives...
2.http://www.css88.com/archives...

兼容多种模块规范(AMD,CMD,Node)的代码相关推荐

  1. 前端模块规范AMD/UMD/CommonJs

    .babelrc文件中的:module设置为false,为什么会要设置成false? 解释:使ES6模块语法转换到另一个模块类型(默认启用"commonjs"). 设置为假则不变换 ...

  2. JavaSript模块规范 - AMD规范与CMD规范介绍[转]

    原文地址:http://blog.chinaunix.net/uid-26672038-id-4112229.html JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什 ...

  3. 按Sybase的PowerDesigner工具设计的数据库模型 --- 解析生成能兼容多种数据库的相应的C#底层代码...

    先有设计,后有代码,改设计总比改代码更容易一些,改设计的成本更低廉,软件也要按图纸施工,没有图纸的建筑物,将来也不好维护,没有数据库设计的软件更怎么可能好维护呢? 1:总希望自己的程序能兼容多种数据库 ...

  4. node中模块、AMD与CMD、ES6模块,node中使用ES6

    1.Nodejs 中的模块 在node环境中一个js文件就是一个模块(module) 我们采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口 node的 ...

  5. 浅析JS模块规范:AMD,CMD,CommonJS

    from:https://www.jianshu.com/p/09ffac7a3b2c 随着JS模块化编程的发展,处理模块之间的依赖关系成为了维护的关键. 模块化 AMD,CMD,CommonJS是目 ...

  6. 再谈 JS中的模块规范(CommonJS,AMD,CMD)来自玉伯的seajs分析

    随着互联网的飞速发展,前端开发越来越复杂.本文将从实际项目中遇到的问题出发,讲述模块化能解决哪些问题,以及如何使用 Sea.js 进行前端的模块化开发. 恼人的命名冲突 我们从一个简单的习惯出发.我做 ...

  7. AMD,CMD,UMD 三种模块规范 写法格式

    一下三块均以 foo.js 为示例文件名,以 jQuery,underscore 为需求组件 ADM:异步模块规范, RequireJs 的支持格式 1 // 文件名: foo.js 2 define ...

  8. Node.js b站教学视频汇总笔记(完)CommonJS模块规范、 require、npm、Express(中间件)、MongoDB、MySQL

    文章目录 Node.js b站教学视频汇总笔记(完)CommonJS模块规范. require.npm.Express(中间件).MongoDB.MySQL 1. Node介绍 为什么要学习Node. ...

  9. 模块格式:CommonJS AMD CMD UMD ES6

    皆为前端工程化中定义模块(module)的规则,如:模块标识,模块依赖,模块实现,模块对外接口,模块加载: 实现时有:异步,同步,依赖前置,依赖就近,预执行,懒执行,软依赖,硬依赖,模块对象支持类型等 ...

最新文章

  1. (10)调用门提权(无参数)
  2. 助力航天元器件管理“高可靠降成本”,赛思库获数千万元Pre-A轮融资
  3. SAP 电商云 Spartacus UI 3.4.5 版本引入的对并发 SSR 请求的支持
  4. Java实现最电话号码的简单加密源码
  5. Couchbase 101:从Java应用程序创建视图(MapReduce)
  6. 开源创新的理念_开源如何解决创新问题
  7. 有关上次的字符串是否相等的比较
  8. JAVA redis 常用函数
  9. 谷歌浏览器批量图片下载插件-合成pdf
  10. canvas简易人机五子棋
  11. 联想笔记本电脑电池修复
  12. Linux快速入门之 内存映射、共享内存(12)
  13. 微生物生态排序分析——CCA分析
  14. 2018年银行业网络金融黑产分析报告
  15. C语言之判断直角三角形
  16. T细胞培养、分离方法大比拼
  17. 创业者该怎么快准狠抓住知识付费项目这个机会变现?
  18. SIMT ( Single Instruction Multiple Threads)
  19. 数据优化| CnOpenData电影评论数据
  20. Visual Studio 2022 正式版下载教程

热门文章

  1. Eclipse中的Git使用之Branch创建,Merge
  2. CSS中选择器的优先级 ( ※关于相同属性的覆盖问题 )
  3. Cocoa异常编程 NSException
  4. hutool BigExcelWriter 下的autoSizeColumnAll异常问题
  5. Selenium常见异常分析及解决方案
  6. 解决“C:\Windows\System32\ntdll.dll”。无法查找或打开 PDB 文件问题
  7. 仿百度文库解决方案(一)——总体思路
  8. nginx 启动报错 “/var/run/nginx/nginx.pid“ failed” 解决方法
  9. Win10远程桌面 出现 身份验证错误,要求的函数不受支持,这可能是由于CredSSP加密Oracle修正 解决方法
  10. 僵尸存在......在.NET中?