モジュールの読み込みと実行は Node.js にパッケージ化されているため、モジュール ファイル内の変数はクロージャ内にあり、グローバル変数を汚染したり他の変数と競合したりすることはありません。
フロントエンド モジュールの場合、開発者は通常、他のモジュールとの競合を避けるためにモジュール コードをクロージャー内に配置します。
Node.js とフロントエンドに共通のモジュールをカプセル化する方法については、Node.js とフロントエンドに共通の関数モジュールである Underscore.js の実装を参照できます。エクスポートが存在するかどうかを判断するために、変数 _ がエクスポートに割り当てられ、古い require() API と下位互換性があります。ブラウザーでは、完全な文字列識別子「_」を介してグローバル オブジェクトとして渡されます。クロージャは次のとおりです:
// 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) は、内部変数がグローバル スコープを汚染するのを避けるために、このオブジェクトの下で関数を呼び出します。ブラウザでは、これはグローバル オブジェクト (ウィンドウ オブジェクト) を指し、外部呼び出し用に "_" 変数がグローバル オブジェクト "root._" に割り当てられます。
Underscore.js に似た Lo-Dash も同様のソリューションを使用しますが、AMD モジュールの読み込みと互換性があります:
(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);
Moment.js のカプセル化クロージャのメイン コードを見てみましょう:
;(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));
上記の例からわかるように、Node.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);
つまり、exports オブジェクトが存在する場合、ローカル変数はエクスポート オブジェクトにロードされます。存在しない場合は、グローバル オブジェクトにロードされます。 ADM 仕様の互換性を追加する場合は、次の一文を追加します:
if (typeof exports !== "undefined") { exports.** = **; } else { this.** = **; }
Node.js の一般的なモジュールのカプセル化方法に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。