首先我們要明白一個(gè)前提,CommonJS模塊規(guī)范和ES6模塊規(guī)范完全是兩種不同的概念。
我們知道在瀏覽器環(huán)境中,使用var聲明一個(gè)全局變量會(huì)把該變量掛載到window對(duì)象上去,所以我們才可以訪問(wèn)到window.a
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script>
var a=1;
console.log(window.a) // 1
</script>
</html>
那如果是在node環(huán)境中呢?
如果我們?cè)谝粋€(gè)js文件中直接打印window是找不到這個(gè)頂層對(duì)象的,因?yàn)閚ode環(huán)境下的頂層對(duì)象是global,
那我們聲明的全局變量會(huì)不會(huì)像window對(duì)象一樣掛載到global對(duì)象上呢?下面我們來(lái)試一下。
var a=1;
console.log(global.a) //undefined
我們要想把變量a掛載到global對(duì)象上就必須要這樣做
//a.js文件
global.a=1;
console.log(global.a)
//注意:掛載到global對(duì)象上的屬性也可以在其他文件被訪問(wèn)到(要先引入)
//b.js文件
reuqire('./a.js')
console.log(global.a)
但是為什么會(huì)是undefined呢?
這就涉及到了模塊化,在node環(huán)境中會(huì)存CommonJS模塊化,它會(huì)把當(dāng)前的文件當(dāng)成模塊的方式加載,那怎么理解以模塊的方式加載呢?
我們可以簡(jiǎn)單的認(rèn)為,每一個(gè)模塊就是一個(gè)函數(shù),模塊中的內(nèi)容就相當(dāng)于是函數(shù)中的內(nèi)容。
this指向問(wèn)題
我們?cè)傧肓硪粋€(gè)問(wèn)題,既然模塊是一個(gè)函數(shù),函數(shù)里的this指向是什么呢?
我們知道函數(shù)中的this是指向調(diào)用它的對(duì)象的,然而每個(gè)模塊又是一個(gè)函數(shù),node環(huán)境中的this又不能指向window,那會(huì)是指向誰(shuí)呢?經(jīng)過(guò)試驗(yàn),發(fā)現(xiàn)node環(huán)境下的this指向的是一個(gè)空對(duì)象,這是因?yàn)閚ode環(huán)境下的this指向的其實(shí)就是該模塊導(dǎo)出的對(duì)象,默認(rèn)是一個(gè)空對(duì)象
console.log(this) //{}
console.log(this === module.exports) //true
console.log(this===exports) //true
//module.exports和exports是js內(nèi)置的兩個(gè)對(duì)象,后面會(huì)詳細(xì)解釋
那我們?nèi)绾巫宼his指向全局對(duì)象global呢?很簡(jiǎn)單,把內(nèi)容放到一個(gè)自執(zhí)行函數(shù)里面就可以了,因?yàn)樽詧?zhí)行函數(shù)里面的this永遠(yuǎn)指向全局對(duì)象
//a.js
(function(){
console.log(this) //global ...(注:...代表省略了其他一些屬性)
})()
argument問(wèn)題
函數(shù)都會(huì)有arguments參數(shù)列表,而模塊作為一個(gè)函數(shù)它的arguments又是什么呢?
//a.js
console.log(arguments)
//會(huì)打印出這么一大堆
[Arguments] {
'0': {},
'1': [Function: require] {
resolve: [Function: resolve] { paths: [Function: paths] },
main: Module {
id: '.',
path: 'D:\\Review',
exports: {},
parent: null,
filename: 'D:\\Review\\test.js',
loaded: false,
children: [],
paths: [Array]
},
extensions: [Object: null prototype] {
'.js': [Function],
'.json': [Function],
'.node': [Function],
'.mjs': [Function]
},
cache: [Object: null prototype] { 'D:\\Review\\test.js': [Module] }
},
'2': Module {
id: '.',
path: 'D:\\Review',
exports: {},
parent: null,
filename: 'D:\\Review\\test.js',
loaded: false,
children: [],
paths: [ 'D:\\Review\\node_modules', 'D:\\node_modules' ]
},
'3': 'D:\\Review\\test.js',
'4': 'D:\\Review'
}
//因?yàn)槊總€(gè)模塊都是一個(gè)函數(shù),由此我們可以推斷出函數(shù)的形參
function(module.exports,require,module,__filename,__dirname){
//參數(shù)注:module.exports={} 導(dǎo)出的對(duì)象
// require 引入的方法
// module module對(duì)象
// __filename 文件路徑
// __dirname 文件夾路徑
}
module.exports和exports的區(qū)別
module和exports是Node.js給每個(gè)js文件內(nèi)置的兩個(gè)對(duì)象
在a.js中用exports或module.exports導(dǎo)出的對(duì)象,可以再另一個(gè)文件中通過(guò)該require()引用。
console.log(module.exports)//{}
console.log(exports) //{}
console.log(module.exports===exports) //true
實(shí)際上,這兩個(gè)對(duì)象指向同一塊內(nèi)存,也就是說(shuō)他們兩個(gè)是等價(jià)的(不去改變他們指向的內(nèi)存地址)
require引入的對(duì)象本質(zhì)上是module.exports.當(dāng)module.exports和exports指向的不是同一塊內(nèi)存,exports導(dǎo)出的內(nèi)容就會(huì)失效。
//a.js
module.exports={name:'xxx'}
exports={name:'ssss'} //同一個(gè)引用被修改,導(dǎo)出內(nèi)容會(huì)失效
//可以用這種寫法 exports.name='sss'
//b.js
let obj=require('./a.js')
console.log(obj.name) //xxx