廖雪峰网站笔记
初识模块
首先编写了一个hello.js文件,这个hello.js文件就是一个模块,模块的名字就是文件名(去掉.js后缀),所以hello.js文件就是名为hello的模块。
把hello.js改造一下,创建一个函数,这样我们就可以在其他地方调用这个函数:
'use strict';
var s = 'Hello';
function greet(name) {
console.log(s + ', ' + name + '!');
}
module.exports = greet;
函数greet()
是我们在hello模块中定义的,最后一行的意思是,把函数greet作为模块的输出暴露出去,这样其他模块就可以使用greet
函数了。
编写一个main.js文件,调用hello模块的greet
函数:
'use strict';
// 引入hello模块:
var greet = require('./hello');
var s = 'Michael';
greet(s); // Hello, Michael!
注意到引入hello模块用Node提供的require函数:
var greet = require('./hello');
引入的模块作为变量保存在greet
变量中,其实变量greet
就是在hello.js中我们用module.exports = greet;
输出的greet
函数。所以,main.js就成功地引用了hello.js模块中定义的greet()
函数,接下来就可以直接使用它了。
在使用require()引入模块的时候,请注意模块的相对路径。因为main.js和hello.js位于同一个目录,所以我们用了当前目录.:
var greet = require(’./hello’); // 不要忘了写相对目录!
如果只写模块名:
var greet = require('hello');
则Node会依次在内置模块、全局模块和当前模块下查找hello.js,你很可能会得到一个错误:
module.js
throw err;
^
Error: Cannot find module 'hello'
at Function.Module._resolveFilename
at Function.Module._load
...
at Function.Module._load
at Function.Module.runMain
遇到这个错误,你要检查:
- 模块名是否写对了;
- 模块文件是否存在;
- 相对路径是否写对了。
CommonJS规范
这种模块加载机制被称为CommonJS规范。在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.js和main.js都申明了全局变量var s = 'xxx'
,但互不影响。
一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable;
,一个模块要引用其他模块暴露的变量,用var ref = require('module_name');
就拿到了引用模块的变量。
结论
要在模块中对外输出变量,用:
module.exports = variable;
输出的变量可以是任意对象、函数、数组等等。
要引入其他模块输出的对象,用:
var foo = require('other_module');
引入的对象具体是什么,取决于引入模块输出的对象。
深入理解
Node可以先准备一个对象module
:
// 准备module对象:
var module = {
id: 'hello',
exports: {
}
};
var load = function (module) {
// 读取的hello.js代码:
function greet(name) {
console.log('Hello, ' + name + '!');
}
module.exports = greet;
// hello.js代码结束
return module.exports;
};
var exported = load(module);
// 保存module:
save(module, exported);
所以:变量module
是Node在加载js文件前准备的一个变量,并将其传入加载函数,我们在hello.js中可以直接使用变量module
原因就在于它实际上是函数的一个参数:module.exports = greet;
通过把参数module
传递给load()
函数,hello.js就顺利地把一个变量传递给了Node执行环境,Node会把module
变量保存到某个地方。
由于Node保存了所有导入的module
,当我们用require()
获取module
时,Node找到对应的module
,把这个module
的exports
变量返回,这样,另一个模块就顺利拿到了模块的输出:
var greet = require('./hello');
module.exports和exports
如果要输出一个键值对象{},可以利用exports
这个已存在的空对象{},并继续在上面添加新的键值;
如果要输出一个函数或数组,必须直接对module.exports
对象赋值。
所以我们可以得出结论:直接对module.exports
赋值,可以应对任何情况:
module.exports = {
foo: function () {
return 'foo'; }
};
或者:
module.exports = function () {
return 'foo'; };
最终,我们强烈建议使用module.exports = xxx
的方式来输出模块变量,这样,你只需要记忆一种方法。