모듈 로딩과 실행은 Node.js에 패키징되어 있어 모듈 파일의 변수는 클로저로 처리되어 전역 변수를 오염시키거나 다른 변수와 충돌하지 않습니다.
프론트엔드 모듈의 경우 개발자는 일반적으로 다른 개발자와의 충돌을 피하기 위해 모듈 코드를 클로저에 배치합니다.
Node.js 및 프런트엔드에 공통된 모듈을 캡슐화하는 방법은 Node.js 및 프런트엔드에 공통된 기능적 기능 모듈인 Underscore.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._ = _; }
내보내기가 존재하는지 판단하여 내보내기에 지역 변수 _를 할당할지 결정합니다. 이전 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);
클로저는 함수 정의를 통해 구성됩니다. call(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-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.** = **; }
즉, 내보내기 개체가 있으면 로컬 변수가 내보내기 개체에 로드되고, 존재하지 않으면 전역 개체에 로드됩니다. ADM 사양의 호환성을 추가한다면 판단 하나를 더 추가하세요:
if (typeof define === "function" && define.amd){}
Node.js의 일반 모듈의 캡슐화 방식에 대한 더 많은 글은 PHP 중국어 홈페이지를 주목해주세요!