We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
主要是为了弥补当前JS没有标准的缺陷,以达到像Python、Ruby和Java具备开发大型应用的基础能力。CommonJS API是以在浏览器环境之外构建 JS 生态系统为目标而产生的项目,比如服务器端JS应用程序、命令行工具、桌面图形界面应用程序等。如今,规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字,单元测试、Web服务器网管接口、包管理等。
CommonJS对模块的定义主要分为模块引用、模块定义和模块标识3个部分
var math = require('math')
在CommonJS规范中,存在一个require()方法,这个方法接收模块标识,一次引入一个模块的API到当前上下文中。
require()
在模块中,上下文提供require()方法引入外部模块。对应引入的功能,上下文提供一个exports对象导出当前模块的方法或变量,并且它是唯一的导出出口,同时模块中还存在一个module对象,它代表的是当前模块,exports是module的属性。所以,在Node中,一个文件就是一个模块,将方法或属性挂载在exports对象上作为属性即可定义导出的方式。
exports
module
// math.js exports.add = function () { var sum = 0, i = 0, args = arguments, l = args.length while (i < 1) { sum += args[i++] } return sum }
在其他文件中,通过require()方法引入模块
// program.js var math = require('math') exports.increment = function (val) { return math.add(val, 1) }
模块标识就是传递给require()方法的参数,采用小驼峰命名,或者以.、..开头的相对路径或绝对路径。它可以不加文件后缀.js。
.
..
CommonJS对模块的定义意义主要在于将类聚的方法或变量等限定在私有的作用域内,同时支持引入和导出功能以顺畅的连接上下游依赖。
CommonJS构建的这套模块导出或引用机制使得用户完全不必考虑变量污染,命名空间等方案与之相比相形见绌。
通常一个module有以下几个属性
module.id 模块的识别符,通常是带有绝对路径的模块文件名
module.filename 模块文件名,带有绝对路径
module.loaded 返回一个boolean值,标识模块是否已经加载完成
module.parent 返回调用该模块的对象
module.children 返回该模块调用的其他模块数组
module.exports 返回该模块对外的输出
module.paths 模块的搜索路径
例如:
// math.js exports.add = function () { var sum = 0, i = 0, args = arguments, l = args.length while (i < 1) { sum += args[i++] } return sum } console.log(module)
它的输出是:
Module { id: '/Users/a123/Desktop/Study/wx/server/math.js', exports: { add: [Function] }, parent: Module { id: '.', exports: {}, parent: null, filename: '/Users/a123/Desktop/Study/wx/server/app.js', loaded: false, children: [ [Module], [Module], [Module], [Module], [Module], [Circular] ], paths: [ '/Users/a123/Desktop/Study/wx/server/node_modules', '/Users/a123/Desktop/Study/wx/node_modules', '/Users/a123/Desktop/Study/node_modules', '/Users/a123/Desktop/node_modules', '/Users/a123/node_modules', '/Users/node_modules', '/node_modules' ] }, filename: '/Users/a123/Desktop/Study/wx/server/math.js', loaded: false, children: [], paths: [ '/Users/a123/Desktop/Study/wx/server/node_modules', '/Users/a123/Desktop/Study/wx/node_modules', '/Users/a123/Desktop/Study/node_modules', '/Users/a123/Desktop/node_modules', '/Users/a123/node_modules', '/Users/node_modules', '/node_modules' ] }
当 Node.js 直接运行一个文件时,require.main 会被设为它的 module。 这意味着可以通过 require.main === module 来判断一个文件是否被直接运行:
require.main
require.main === module
// node app.js math---require.main === module : false app---require.main === module : true
module.exports 对象是由模块系统创建的,表示当前文件对外输出的接口。
注意,对 module.exports 的赋值必须立即完成。 不能在任何回调中完成。
module.exports
// 创建a.js文件 const EventEmitter = require('events') module.exports = new EventEmitter() // 赋值 // 处理一些工作,并在一段时间后从模块自身触发 'ready' 事件。 setTimeout(() => { module.exports.emit('ready') }, 1000)
// 创建b.js文件 setTimeout(() => { module.exports = {a: 'hello'} }, 0)
// app.js 文件中分别调用a、b模块 // 引入a模块 const a = require('./a') a.on('ready', () => { console.log('模块 a 已准备好') }) // 引入b模块 const b = require('./b') console.log(b.a)
执行app.js结果是:
undefined 模块 a 已准备好
exports 变量是在模块的文件级别作用域内有效的,它在模块被执行前被赋予 module.exports 的值。
例如: module.exports.fun = …,相当于exports.fun = ...
module.exports.fun = …
exports.fun = ...
但注意,不能将一个值赋值给exports,这样它将不在绑定到module.exports
module.exports.hello = true; // 从对模块的引用中导出 exports = { hello: false }; // 不导出,只在模块内有效
在上述介绍中,module.exports 与exports很容易混淆,下面介绍module.exports 与exports内部的实现:
// 1. 模块引用 var module = require('./a') module.a // 重要的是 module 这里,module 是 Node 独有的一个变量 // 2. 模块定义 module.exports = { a: 1 } // 3.模块内部实现 function require(/* ... */) { const module = { exports: {} // exports 就是一个空对象 } // 这里其实就是包装了一层立即执行函数,这样就不会污染全局变量了 ((module, exports) => { // 模块代码在这 var a = 1 // 如果定义了一个函数。则为 function someFunc() {} exports = a; // 此时,exports 不再是一个 module.exports 的快捷方式, // 且这个模块依然导出一个空的默认对象。 module.exports = a; // 这个是为什么 exports 和 module.exports 用法相似的原因 // 此时,该模块导出 a,而不是默认对象。 })(module, module.exports); return module.exports; }
require命令的功能是,引入模块。
require
require命令用于加载文件,后缀名默认为.js。
.js
require的加载顺序是:
以/开头,加载绝对路径模块文件
/
以./开头,加载相对路径模块文件
./
除以上两种情况外,加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
举例来说,脚本/Users/a123/projects/foo.js执行了require('bar.js')`命令,Node会依次搜索以下文件。
执行了
如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。
.json
.node
如果想得到require命令加载的确切文件名,使用require.resolve()方法。
require.resolve()
通常,我们会把相关的文件放在一个目录里面,便于组织,这就需要给该目录设置一个入口文件,可以让require方法通过这个入口文件,加载整个目录,例如package.json:
package.json
{ "name": "wx_backend", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "start:node-dev": "pm2 start process.prod.json --no-daemon --env development", "start:node-prod": "pm2 start process.prod.json --no-daemon --env production", "dev": "nodemon --config nodemon.json app.js", "initdb": "npm install && node tools/initdb.js" }, ... }
require发现参数字符串指向一个目录时,就会自动查看该目录下的package.json文件,然后加载main指定的入口文件,如果package.json中没有main字段,或者根本没有package.json文件,则会加载该目录下的index.js文件或index.node文件。
main
index.js
index.node
具体可查阅nodeJS
前后端 JS 分别搁置在 HTTP 的两端,它们扮演的角色不同,侧重点也不一样。 浏览器端的 JS 需要经历从一个服务器端分发到多个客户端执行,而服务器端 JS 则是相同的代码需要多次执行。前者的瓶颈在于宽带,后者的瓶颈则在于 CPU 等内存资源。前者需要通过网络加载代码,后者则需要从磁盘中加载, 两者的加载速度也不是在一个数量级上的。 纵观 Node 的模块引入过程,几乎全都是同步的,尽管与 Node 强调异步的行为有些相反,但它是合理的,但前端如果也用同步方式引入,试想一下,在 UI 加载的过程中需要花费很多时间来等待脚本加载完成,这会造成用户体验的很大问题。 鉴于网络的原因, CommonJS 为后端 JS 制定的规范并不完全适合与前端的应用场景,下面来介绍 JS 前端的规范。
AMD是由RequireJS提出的,相对于CommonJS同步加载来说,AMD是"Asynchronous Module Definition"(异步模块定义)的缩写。是为浏览器环境专门设计的。
RequireJS即为遵循AMD规范的模块化工具。 RequireJS的基本思想是,通过一个函数来将所有所需要的或者说所依赖的模块实现装载进来,然后返回一个新的函数(模块),我们所有的关于新模块的业务代码都在这个函数内部操作,其内部也可无限制的使用已经加载进来的以来的模块。
define(id?, dependencies?, factory)
定义模块
定义无依赖模块
define( { sum : function( x, y ){ return x + y ; } } );
定义有依赖模块
define(["some"], function( alpha ){ return { add : function(){ return some.sum() + 1 ; } } });
定义数据对象模块
define({ add: [], sub: [] });
具名模块
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){ export.verb = function(){ return beta.verb(); // or: return require("beta").verb(); } });
包装模块
define(function(require, exports, module) { var a = require('a'), b = require('b'); exports.action = function() {}; } );
不考虑多了一层函数外,格式和Node.js是一样的:使用require获取依赖模块,使用exports导出API。
除了define外,AMD还保留一个关键字require。require 作为规范保留的全局标识符,可以实现为 module loader,也可以不实现。
模块加载
require([module], callback)
require(['math'], function(math) { math.add(2, 3); });
CMD 的规范由国内的玉伯提出,与 AMD 的区别主要在于 定义模块与依赖引入 的部分。
CMD 更接近与 Node 对 CommonJS 规范的定义:
define(factory)
在依赖部分,CMD 支持动态引入
define(function(require, exports, module) { // 模块内容 }
require、exports、module 通过形参传递给模块,在需要依赖模块时,可以通过 require() 引入。
ES6正式提出了内置的模块化语法。
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
"use strict";
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
export
import
//导出变量 export var a = 1; //导出函数 export function fun(){ ... } //导出类 export class Rectangle { ... } function fun1() { ... } function fun2() { ... } //导出对象,即导出引用 export {fun1 as f1, fun2 as f2} // 重命名模块 // 导出默认值 export default function fun3() { ... } // 错误, 后面不能跟变量声明语句。 export default var a = 1 // 正确 export default 42
另外,export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export var foo = 'bar'; setTimeout(() => foo = 'baz', 500);
上面代码输出变量foo,值为bar,500毫秒之后变成baz。
foo
bar
baz
// 第一组 example.js export default function fun() { // 输出 // ... } // app.js import fun from './example'; // 第二组 example.js export function fun() { // 输出 // ... }; // app.js import {fun} from './example'; // 输入 // 或 import * as allFun from './example'; //allFun.fun
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能对应一个方法。
export default
本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。
default
// modules.js function fun() { ... } export {fun as default}; // 等同于 // export default fun; // app.js import { default as fun } from './modules'; // 等同于 // import fun from 'modules';
CommonJS和ES6中模块化的两者区别
CommonJS
require(${path}/xx.js)
require/exports
The text was updated successfully, but these errors were encountered:
good
Sorry, something went wrong.
best
No branches or pull requests
一、出发点
主要是为了弥补当前JS没有标准的缺陷,以达到像Python、Ruby和Java具备开发大型应用的基础能力。CommonJS API是以在浏览器环境之外构建 JS 生态系统为目标而产生的项目,比如服务器端JS应用程序、命令行工具、桌面图形界面应用程序等。如今,规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字,单元测试、Web服务器网管接口、包管理等。
二、CommonJS的模块规范
CommonJS对模块的定义主要分为模块引用、模块定义和模块标识3个部分
1. 模块引用
在CommonJS规范中,存在一个
require()
方法,这个方法接收模块标识,一次引入一个模块的API到当前上下文中。2. 模块定义
在模块中,上下文提供
require()
方法引入外部模块。对应引入的功能,上下文提供一个exports
对象导出当前模块的方法或变量,并且它是唯一的导出出口,同时模块中还存在一个module
对象,它代表的是当前模块,exports
是module
的属性。所以,在Node中,一个文件就是一个模块,将方法或属性挂载在exports
对象上作为属性即可定义导出的方式。在其他文件中,通过
require()
方法引入模块3. 模块标识
模块标识就是传递给
require()
方法的参数,采用小驼峰命名,或者以.
、..
开头的相对路径或绝对路径。它可以不加文件后缀.js。CommonJS对模块的定义意义主要在于将类聚的方法或变量等限定在私有的作用域内,同时支持引入和导出功能以顺畅的连接上下游依赖。
CommonJS构建的这套模块导出或引用机制使得用户完全不必考虑变量污染,命名空间等方案与之相比相形见绌。
三、module 对象
1.module
通常一个
module
有以下几个属性module.id 模块的识别符,通常是带有绝对路径的模块文件名
module.filename 模块文件名,带有绝对路径
module.loaded 返回一个boolean值,标识模块是否已经加载完成
module.parent 返回调用该模块的对象
module.children 返回该模块调用的其他模块数组
module.exports 返回该模块对外的输出
module.paths 模块的搜索路径
例如:
它的输出是:
当 Node.js 直接运行一个文件时,
require.main
会被设为它的module
。 这意味着可以通过require.main === module
来判断一个文件是否被直接运行:2. module.exports
module.exports 对象是由模块系统创建的,表示当前文件对外输出的接口。
注意,对
module.exports
的赋值必须立即完成。 不能在任何回调中完成。执行app.js结果是:
3. exports
exports
变量是在模块的文件级别作用域内有效的,它在模块被执行前被赋予module.exports
的值。例如:
module.exports.fun = …
,相当于exports.fun = ...
但注意,不能将一个值赋值给
exports
,这样它将不在绑定到module.exports
4. module.exports与exports
在上述介绍中,
module.exports
与exports
很容易混淆,下面介绍module.exports
与exports
内部的实现:四、require
require
命令的功能是,引入模块。require
命令用于加载文件,后缀名默认为.js
。require
的加载顺序是:以
/
开头,加载绝对路径模块文件以
./
开头,加载相对路径模块文件除以上两种情况外,加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
举例来说,脚本/Users/a123/projects/foo.js
执行了
require('bar.js')`命令,Node会依次搜索以下文件。如果指定的模块文件没有发现,Node会尝试为文件名添加
.js
、.json
、.node
后,再去搜索。.js
件会以文本格式的JavaScript脚本文件解析,.json
文件会以JSON格式的文本文件解析,.node
文件会以编译后的二进制文件解析。如果想得到
require
命令加载的确切文件名,使用require.resolve()
方法。通常,我们会把相关的文件放在一个目录里面,便于组织,这就需要给该目录设置一个入口文件,可以让
require
方法通过这个入口文件,加载整个目录,例如package.json
:require
发现参数字符串指向一个目录时,就会自动查看该目录下的package.json
文件,然后加载main
指定的入口文件,如果package.json
中没有main
字段,或者根本没有package.json
文件,则会加载该目录下的index.js
文件或index.node
文件。具体可查阅nodeJS
五、扩展
前后端 JS 分别搁置在 HTTP 的两端,它们扮演的角色不同,侧重点也不一样。 浏览器端的 JS 需要经历从一个服务器端分发到多个客户端执行,而服务器端 JS 则是相同的代码需要多次执行。前者的瓶颈在于宽带,后者的瓶颈则在于 CPU 等内存资源。前者需要通过网络加载代码,后者则需要从磁盘中加载, 两者的加载速度也不是在一个数量级上的。
纵观 Node 的模块引入过程,几乎全都是同步的,尽管与 Node 强调异步的行为有些相反,但它是合理的,但前端如果也用同步方式引入,试想一下,在 UI 加载的过程中需要花费很多时间来等待脚本加载完成,这会造成用户体验的很大问题。
鉴于网络的原因, CommonJS 为后端 JS 制定的规范并不完全适合与前端的应用场景,下面来介绍 JS 前端的规范。
1. AMD
AMD是由RequireJS提出的,相对于CommonJS同步加载来说,AMD是"Asynchronous Module Definition"(异步模块定义)的缩写。是为浏览器环境专门设计的。
RequireJS即为遵循AMD规范的模块化工具。 RequireJS的基本思想是,通过一个函数来将所有所需要的或者说所依赖的模块实现装载进来,然后返回一个新的函数(模块),我们所有的关于新模块的业务代码都在这个函数内部操作,其内部也可无限制的使用已经加载进来的以来的模块。
define(id?, dependencies?, factory)
定义模块
定义无依赖模块
定义有依赖模块
定义数据对象模块
具名模块
包装模块
不考虑多了一层函数外,格式和Node.js是一样的:使用require获取依赖模块,使用exports导出API。
除了define外,AMD还保留一个关键字require。require 作为规范保留的全局标识符,可以实现为 module loader,也可以不实现。
模块加载
require([module], callback)
2. CMD
CMD 的规范由国内的玉伯提出,与 AMD 的区别主要在于 定义模块与依赖引入 的部分。
CMD 更接近与 Node 对 CommonJS 规范的定义:
define(factory)
在依赖部分,CMD 支持动态引入
require、exports、module 通过形参传递给模块,在需要依赖模块时,可以通过
require()
引入。3. ES6 模块化
ES6正式提出了内置的模块化语法。
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上
"use strict";
。模块功能主要由两个命令构成:
export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。export
另外,
export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。上面代码输出变量
foo
,值为bar
,500毫秒之后变成baz
。import
export default
命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default
命令只能使用一次。所以,import
命令后面才不用加大括号,因为只可能对应一个方法。本质上,
export default
就是输出一个叫做default
的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。CommonJS
和ES6中模块化的两者区别require(${path}/xx.js)
,后者目前不支持,但是已有提案require/exports
来执行的The text was updated successfully, but these errors were encountered: