モジュールの読み込みと実行は Node.js にパッケージ化されているため、モジュール ファイル内の変数はクロージャー内にあり、グローバル変数を汚染したり他の変数と競合したりすることはありません。
フロントエンド モジュールの場合、開発者は通常、他のモジュールとの競合を避けるためにモジュール コードをクロージャー内に配置します。
Node.js とフロントエンドに共通のモジュールをカプセル化する方法は、Node.js とフロントエンドに共通の関数モジュールである Underscore.js 実装を参照できます。コードを表示します。
// アンダースコアへの安全な参照を作成します。以下で使用するオブジェクト。
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// **Node.js** の Underscore オブジェクトを、
付きでエクスポートします // 古い `require()` API との下位互換性.
// ブラウザにいる場合は、文字列識別子を介してグローバル オブジェクトとして `_` を追加します。
// Closure Compiler の「高度な」モードの場合。
if (typeof exports !== '未定義' ) {
if (モジュールのタイプ !== '未定義' && module.exports) {
エクスポート = module.exports = _;
}
エクスポート._ = _;
} else {
root._ = _;
}
ローカル変数 _ がエクスポートに割り当てられているかどうかを判断するために、エクスポートが存在するかどうかを判断します。これは、古い require( と下位互換性があります。 ) API。ブラウザ内の場合、文字列識別子「_」を介したグローバル オブジェクトとして、完全なクロージャは次のようになります:
(function() {
// ベースライン設定
// ------------ --
// ルート オブジェクト、ブラウザの `window`、またはサーバーの `exports` を確立します。
var root = this;
// 安全な参照を作成します以下で使用する Underscore オブジェクトに変換します。
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// **Node.js** の Underscore オブジェクトをエクスポートします。
// 古い `require( )` API。
// ブラウザの場合、文字列識別子を介してグローバル オブジェクトとして `_` を追加します。
// Closure Compiler の「高度な」モードの場合。
if (typeof エクスポート) !== '未定義') {
if (モジュールのタイプ !== '未定義' && module.exports) {
エクスポート = module.exports = _;
}
エクスポート._ = _ ;
} else {
root._ = _;
}
}).call(this);
クロージャは関数を通じて構築されます定義。call(this) は、内部変数がグローバル スコープを汚染するのを避けるために、このオブジェクトの下で呼び出される関数です。ブラウザでは、これはグローバル オブジェクト (ウィンドウ オブジェクト) を指し、外部呼び出し用に "_" 変数がグローバル オブジェクト "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-Dash
var _ = 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" module
define(function() {
return _;
});
}
// check for `exports` after `define` in case a build optimizer adds an `exports` object
else if (freeExports && freeModule) {
// in Node.js or RingoJS
if (moduleExports) {
(freeModule.exports = _)._ = _;
}
// in Narwhal or Rhino -require
else {
freeExports._ = _;
}
}
else {
// in a browser or Rhino
root._ = _;
}
}.call(this));
再来看看Moment.js的封装闭包主要代码:
(function (undefined) {
var moment;
// check for nodeJS
var 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" mode
if (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 defined
if (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 global
makeGlobal(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){}