NODEJS_模块使用

1.1、JavaScript的模块规范

目前流行的JavaScript的模块规范主要有两种:
CommonJS
CommonJS把每一个文件看做一个模块,模块内部定义的每一个变量都是私有的,无法别其他模块使用,除非使用预定义的方法将内部定义的变量暴露出来(通过exprots和require关键字来实现
CommonJS 一个显著的特点就是模块加载是同步的,就目前来说,受限制于宽带速度,并不适用于浏览器中的人JavaScript。
AMD
AMD是Asynchronous Module Definition的缩写,意思就是“一步模块定义”,它采用异步方式加载模块,模块加载不影响它后面语句的运行。依赖这个模块代码定义在一个回调函数中,等到加载完成之后,这个回调才会运行。

1.2、require及其运行机制

1
2
3
4
5
6
7
8
9
10
11
//preson.js
var preson ={
talk: function () {
console.log("I am talking...");
},
listen :function () {
console.log("I am listening...");
}
//MORE func...
}
module.exports = person;

这样实现一个自定义的模块,该模提供的一个preson接口,然后使用module.exports将接口口暴露给外部有使用,外部的代码想要使用preson.js的方法,需要使用require关键字引入该接口。

1
2
var persom =requitr("./person.js");
person.talk();

注意:再引入自定义模块时省略相对路径“./”会导致错误。
如果只需要引入其中的人一个,代码可以这样写:

1
2
var talk =require("./person.js").talk;
talk();

require关键字并不依赖于exports,应为加载一个没有暴露任何方法的模块,这相当于执行一个模块代码,这样做通常是没有意义的。

1.3、require的缓存策略

Node会自动缓存require引入的文件,使得下次再引入不需要在经过文件系统而是直接从缓存读取。这种缓存是基于文件路径定位的,这表示两个完全相同的文件,但在他们位于不同的路径下,也会在缓存中维持两份。例如使用下面代码访问目前在缓存中的文件:

1
2
var moudle =require("./moudle.js");
console.log(require.cache);

1.4、require的隐患

当调用require加载一个模块时,模块内部的代码会被调用,有时候会带来隐藏的bug。例如下面的代码:

1
2
3
4
5
6
7
8
9
10
//module.js
function test(){
setInterval(function (){
console.log("test");
}),100
}
test();
module.expoets =test;
//run.js
var test require("./module.js");

run.js除了加载一个模块之外没有进行任何操作,试着运行一下,就会发现每隔一秒钟就会输出一个 “test”字符串,同时run.js的进程不会退出。因为:加载一个模块相当于执行模块内部代码,在module.js中由于设置了一个不间断的定时器,导致run.js也会一直运行。

1.5、模块化下的作用域

主要关注点在this上。

Node和javaScript中的this指向上有一些区别,其中Node控制台和脚本文件的策略不尽相同。对于浏览器中的javaScript来说,无论是在脚本还是在浏览器控制台中,其this的指向是一致的,然Node并非这样。

1.控制台中的this

1
2
3
4
5
6
console.log(this);
//输出:window{stop:functiom,open function,.....}
//---------------------
var a = 10;
console.log(this.a);
//输出 10;

2.Node控制台中的this

在node控制台中,全局变量会挂到global下.

创建一个test.js文件在文件中添加以下代码:

1
console.log(this);//{}

运行 node test.js打印出结果是一个空对象。
执行下面代码:

1
2
3
var a = 10;
console.log(this.a);//undefined
console.log(global.a);//undefined

结果全部都是undefined,说明定义的变量a并没有挂到全局变量的this或者global对象。
然而如果声明变量时不使用var或者let关键字修饰,例如下面代码:

1
2
a = 10;
console.log(global.a);//10

这样却可以正常打印结果。
node脚本文件中定义的全局this指向是:module.exports。如下面代码所示:

1
2
this.a = 10;
console.log(module.exports);//10

1.6、Node中作用域种类

1.全局作用域

1
2
a = 10;
console.log(global.a);//10

2.模块作用域

1
2
this.a = 10;
console.log(module.exports);//10

3.函数作用域

这个大家都比较熟悉,就不在介绍。

4.块级作用域

4.1ES5中的作用域

ES5中只有两种作用域,全局作用域和函数作用域,例如在一个js文件中声明的一个变量:

1
var names= "later";

代码中的name变量属于全局作用域,这个表示在同一个文件的任何位置都可以访问待该变量,如果想创建一个新的作用域,只能通过创建一个新的函数来实现。

1
2
3
4
5
6
7
var mname="mname";
function tesdt(){
var mnmae="Less";
console.log(mname);
}
test();
console.log(mname);

上面代码中的两个name属于不同的作用域,在test中修改mname不会影响到全局的变量。
4.1.1块级作用域
大多数常用编辑语言中都有会块级作用域的概念,以C语言为例,C语言可以用一对花括号来定义一个块级作用域,这表示一个for循环或者一个if代码都会定义独立的块级作用域,而在ES5中没有这样的设计。

1
2
3
4
5
if(true){
var x= 2;
console.log(x);//2
}
console.log(x);//2

在IF代码中的变量X在外部依然可以访问到(在Java中会抛出未定义的错误),这表if代码块也属于全局作用域。要想避免这种情况,通常使用闭包来隔离作用域,如下面的代码所示:

1
2
3
4
5
6
7
8
9
10
function (){
var x=1;
if(true){
(function (){
var x= 2;
console.log(x);//2
}());
}
console.log(x);//1
}

要想实现一个闭包要添加不少冗余代码。

4.1.2变量提升
变提升是指javaScript解析器会将变量的声明提前到当前作用域最前的现象,node也继承了这一点。

1
2
console.log(msg);
var msg="I get you ";

打印出的undefuned。
实际上,上面的代码的代码中存在一个隐式的变量提升,变量的声明被提到代码的最前面,实际上执行的是以下形式:

1
2
3
var msg;
console.log(msg);
var msg="I get you ";

变量提升变量提升仅仅提升变量的声明,不会提升面量的值。

4.2let关键
ES2015中引入let关键字提供了对块级作用域的支持。
let关键字会创建一个块级作用域,使用let关键字声明变量只能在当前块级作用域使用,代码如下:

1
2
3
4
5
var i=1;
for (let i=0;i<3;i++>){
console.log(i);//0,1,2
}
console.log(i);//0

for循环内部使用let声明变量i,和外部var声明变量i处于不同的作用域心中,他们的值付不影响;如果将let改成var那么最后打印的结果是三而不是0

4.2.1重复声明
在同一个块级作用域中,不能使用let关键字重复声明一个变量。在不用的块级作用域则无此限制。

1
2
3
4
5
6
7
8
9
10
11
12
let as="121";
let as="304";//SyntaxError:Identifier "a" has already been declared
//================================

let i=1;
function scop(){
let i=3;
for (let i=0;i<3;i++>){
let i=5;
}
console.log(i);//1
}

4.2.2重复声明

1
2
console.log(a);
let a=10;//ReferenceError: a is not defined

代码解析器会看成:

1
2
3
let a;
console.log(a);
a=10;

结果就是和没有赋值是一样的。