转自:《如何封装Node.js和前端通用的模块》
在Node.js中对模块载入和执行进行了包装,使得模块文件中的变量在一个闭包中,不会污染全局变量,和他人冲突。
前端模块通常是我们开发人员为了避免和他人冲突才把模块代码放置在一个闭包中。
如何封装Node.js和前端通用的模块,我们可以参考Underscore.js 实现,他就是一个Node.js和前端通用的功能函数模块,查看代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 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,如果在浏览器中,通过一个字符串标识符“_”作为一个全局对象;完整的闭包如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
(
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模块载入的兼容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
;
(
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的封装闭包主要代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
(
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和前端通用的模块时,可以使用以下逻辑:
1
2
3
4
5
|
if
(
typeof
exports
!==
"undefined"
)
{
exports
.
*
*
=
*
*
;
}
else
{
this
.
*
*
=
*
*
;
}
|
即,如果exports对象存在,则将局部变量装载在exports对象上,如果不存在,则装载在全局对象上。如果加上ADM规范的兼容性,那么多加一句判断:
1
|
if
(
typeof
define
===
"function"
&&
define
.
amd
)
{
}
|