@wy
2018-07-16T10:09:12.000000Z
字数 2645
阅读 529
nodejs
Javascript 应用在浏览器端时,划分成多个文件,使用 script 标签引入。在每一个引入的js文件都没有独立的作用域,这导致在之后引入文件的变量会覆盖之前引入文件的变量,例如:
// a.js 文件
var a = 10;
document.onclick = function(){
alert(a);
}
在 a.js 文件的需求中,点击后应该输出是10。
// b.js 文件
var a = 20;
现在有两个文件,在一个html中引入:
<script src="a.js"></script>
<script src="b.js"></script>
此时再次点击 document, 弹出的就不是10,而是20了。因为两个文件定义在函数外的变量都属于全局变量,就产生了覆盖。
在浏览器中怎么做到模块化,让一个文件有自己独立的作用域呢?答案是使用立即执行函数表达式(IIFE)的方式,稍加改造:
// a.js 文件
;(function(){
var a = 10;
document.onclick = function(){
alert(a);
}
})();
这样变量 a 就不是个全局变量,只能在这个函数作用域下使用,就不会有被别的文件覆盖的风险。
在 Node 中不使用script标签引入文件,Node 应用由模块组成,需要有对模块的定义和引用。
扩展知识:
1. 什么是模块?
为了编写可维护可复用的代码,会把很多代码或函数按功能划分到不同的文件中,这样在每个文件中包含的代码就相对较少,这一个文件称之为是一个模块,很多编程语言都采用这种模块化的方式来组织代码。
2. 使用模块有什么好处?
首先,一个模块代码量少了,会提高可维护性,查找修改都相对容易。其次是,编写一个模块后,可以在使用到的地方复用模块。还有好处就是每个模块都有独立的作用域,可以避免函数名和变量名冲突,相同名字的函数和变量完全可以分别存在不同的模块中。
Node 为服务端的 Javascript制定了一些规范,并借鉴了 CommonJS 标准中的模块规范实现了一套非常易用的模块系统。
CommonJS 对模块的定义十分的简单,主要分为 模块引用、模块定义和模块标识3个部分。
CommonJS 模块规范中,使用 require 函数来加载一个模块,传入模块标识即可。require 基本功能是,读入并执行一个 JavaScript 文件,执行之后返回模块对象(module.exports),可以将这个模块对象赋给一个局部变量,示例如下:
// app.js 模块
const tools = require('./tools.js')
会得到加载文件 tools.js 的模块对象,只需要在文件中把要对外暴露的API挂载在模块对象 module.exports 上。
关于模块标识在 node 中并不止这一种形式,稍后讨论。
一个文件就是一个模块,有自己的独立的作用域,定义在文件中的标识符(变量名、函数名)都是私有的,对其他文件不可见。
// tools.js 文件
let test = 'hello'
let add = (a,b) => {
return a + b;
}
// 在app.js中使用
const tools = require('./tools.js');
let test = 'app.js中的test'
以上在 app.js 文件中使用了 tools.js ,虽然两个文件有相同的变量,但并不会产生变量污染,因为这两个文件有各自独立的作用域。
在 app.js 中需要使用 tools.js 中定义的函数,要在 tools.js 中 对外暴露指定的函数。
CommonJS规范规定,每个模块内部,module 变量代表当前模块,这个变量是一个对象,它的 exports 属性(即module.exports)是模块对外提供的出口,加载某个模块,其实是加载该模块的module.exports属性对应的值,默认是一个对象。
将方法挂载在 module.exports 对象上作为属性的方式导出:
// tools.js 文件
let test = 'hello'
let add = (a,b) => {
return a + b;
}
module.exports.add = add;
// 在app.js中使用
const tools = require('./tools.js');
let test = 'app.js中的test'
// 用add方法
tools.add(1,2)
在 app.js 加载模块的 require 函数执行后,实际上返回的就是加载的模块中的 module.exports 对象。
可以做如下的验证:
// tools.js 文件
module.exports = 'hello'; // 改写模块的 exports 对象
// 在app.js中使用
const tools = require('./tools.js');
console.log(tools)
利用 node app.js 跑一下文件,会在控制台输出是 1。由此可以验证加载的模块中的 module.exports 的值。
模块标识就是传递给 require 函数的参数,目的是标识模块所在的位置。require 命令用于加载模块,后缀名默认为.js,也就是在加载 js 文件,可以省略后 .js 后缀。
const tools = require('./tools');
根据传入给require 函数的参数的不同格式,require 命令去不同路径寻找模块文件。
node 对 CommonJS 规范根据自身特征进行了取舍。
node 中模块分为三类,核心模块、文件模块、文件夹模块。
加载这类模块,只需要写入模块名即可。
const http = require('http');
加载这类模块,要写上相对或绝对路径,以便区分核心模块和文件夹模块。
const tools = require('./tools');
使用 NPM 会在安装模块的文件夹下创建 node_modules ,用以存放使用 NPM 安装的模块。
加载这类模块,只需要写上模块名即可。
const vue = require('vue');
加载文件夹模块和核心模块都是提供模块名,这有什么不同吗?看一下它们的查找规则。