很全很全的JavaScript的模块讲解

作者:信息技术

本文富含两局部,第风度翩翩局地由此明显的陈述介绍如何是 CommonJS、英特尔、CMD、UMD、ES Module 以致它们的宽广用法,第二有个别则依照实际难题建议在正规的 webpack 构建进度中该怎么钦命打包配置中的模块化参数。

介绍

模块常常是指编程语言所提供的代码协会体制,利用此编写制定可将顺序拆解为单身且通用的代码单元。所谓模块化首倘诺缓慢解决代码分割、功能域隔绝、模块之间的依据管理以致发布到临盆条件时的自动化打包与拍卖等三个方面。

模块的长处

1.可维护性。 因为模块是独立的,叁个安排精良的模块会让外部的代码对和煦的正视越少越好,那样和和气气就足以独立去创新和改善。
2.命名空间。 在 JavaScript 里面,如若三个变量在最拔尖的函数之外注解,它就径直形成全局可用。由此,平时相当大心现身命名冲突的情景。使用模块化开荒来封装变量,能够制止污染全局情形。
3.重用代码。 我们不经常会赏识从从前写过的门类中拷贝代码到新的项目,那未尝难题,不过越来越好的秘技是,通过模块援用的章程,来制止重新的代码库。

前面贰个发展到今日,已经有超级多模块化的方案,举例Intel、CMD、UMD、CommonJS等,当然了,还大概有es6带动的模块系统,那个模块化标准的为主价值都以让 JavaScript 的模块化开拓变得轻易和自然,前不久就来走访这么些规范都是啥。

JavaScript 模块化方案

CommonJS

CommonJS 最开首是 Mozilla 的程序猿于 贰零零捌年开首的叁个品种,它的目标是让浏览器之外的 JavaScript (比方服务器端大概桌面端)可以通过模块化的措施来开拓和同盟。

在 CommonJS 的正统中,每一个 JavaScript 文件就是多个独门的模块上下文(module context),在这里个上下文中私下认可成立的性质都以私有的。也正是说,在二个文书定义的变量(还包涵函数和类),都以个体的,对其余文件是不可见的。

内需小心的是,CommonJS 规范的最首要适用途景是服务器端编制程序,所以使用一块加载模块的政策。假如大家依附3个模块,代码会八个叁个逐项加载它们。

该模块完毕方案主要包涵 require 与 module 那七个重大字,其允许有些模块对外暴露部分接口何况由别的模块导入使用。

//sayModule.js
function SayModule () {
    this.hello = function () {
        console.log('hello');
    };

    this.goodbye = function () {
        console.log('goodbye');
    };
}

module.exports = SayModule;

//main.js 引入sayModule.js
var Say = require('./sayModule.js');
var sayer = new Say();
sayer.hello(); //hello

作为一个服务器端的解决方案,CommonJS 要求叁个天造地设的本子加载器作为前提条件。该脚本加载器必得支持名称为 require 和 module.exports 的函数,它们将模块互相导入导出。

Node.js

Node 从 CommonJS 的有的创新意识中,创建出本人的模块化达成。由于Node 在服务端的流行,Node 的模块方式被(不精确地)称为 CommonJS。

Node.js模块能够分为两大类,风姿洒脱类是核心模块,另风华正茂类是文本模块。
大旨模块 正是Node.js标准的API中提供的模块,如fs、http、net等,这个都以由Node.js官方提供的模块,编写翻译成了二进制代码,能够一向通过require获取基本模块,比方require('fs'State of Qatar,宗旨模块具备最高的加载优先级,假若有模块与大旨模块命名冲突,Node.js总是会加载核心模块。
文本模块 是存款和储蓄为单独的文书(或文件夹)的模块,可能是JavaScript代码、JSON或编写翻译好的C/C++代码。在不显式内定文件模块扩充名的时候,Node.js会分别总计加上.js、.json、.node(编写翻译好的C/C++代码State of Qatar。

加载形式

  • 按路线加载模块

假定require参数少年老成"/"起先,那么就以相对路线的法子查找模块名称,假使参数生机勃勃"./"、"../"起始,那么则是以相对路线的主意来寻找模块。

  • 通过搜索node_modules目录加载模块

假诺require参数不以"/"、"./"、"../"开首,而该模块又不是着力模块,那么就要通过寻觅node_modules加载模块了。大家选用的npm获取的包平时正是以这种办法加载的。

加载缓存

Node.js模块不会被再一次加载,那是因为Node.js通过文件名缓存全数加载过的公文模块,所以事后再拜谒届时就不会再一次加载了。
注意: Node.js是基于实际文件名缓存的,实际不是require(卡塔尔提供的参数缓存的,也等于说就算你分别通过require('express'State of Qatar和require('./node_modules/express'卡塔尔加载若干次,也不会再次加载,因为纵然三遍参数差别,深入深入分析到的文书却是同叁个。

Node.js 中的模块在加载之后是以单例化运转,而且遵照值传递原则:假设是一个指标,就一定于那么些目标的引用。

模块载入进程

加载文件模块的行事,首要由原生模块module来兑现和到位,该原生模块在运行时已经被加载,进程平素调用到runMain静态方法。

例如运行: node app.js

Module.runMain = function () {
    // Load the main module--the command line argument.
    Module._load(process.argv[1], null, true);
};

//_load静态方法在分析文件名之后执行
var module = new Module(id, parent);

//并根据文件路径缓存当前模块对象,该模块实例对象则根据文件名加载。
module.load(filename);

切实说一下上文提到了文件模块的三类模块,这三类文件模块然后缀来分别,Node.js会根据后缀名来支配加载方法,具体的加载方法在下文require.extensions中会介绍。

  • .js 通过fs模块同步读取js文件并编写翻译实行。
  • .node 通过C/C++举办编辑的Addon。通过dlopen方法开展加载。
  • .json 读取文件,调用JSON.parse解析加载。

接下去详细描述js后缀的编写翻译过程。Node.js在编写翻译js文件的经过中实际上到位的步调有对js文件内容进行头尾包装。以app.js为例,包装之后的app.js将会化为以下格局:

//circle.js
var PI = Math.PI;
exports.area = function (r) {
    return PI * r * r;
};
exports.circumference = function (r) {
    return 2 * PI * r;
};

//app.js
var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is ' + circle.area(4));

//app包装后
(function (exports, require, module, __filename, __dirname) {
    var circle = require('./circle.js');
    console.log('The area of a circle of radius 4 is ' + circle.area(4));
});

//这段代码会通过vm原生模块的runInThisContext方法执行(类似eval,只是具有明确上下文,不污染全局),返回为一个具体的function对象。最后传入module对象的exports,require方法,module,文件名,目录名作为实参并执行。

那便是怎么require并不曾概念在app.js 文件中,可是那一个办法却存在的缘故。从Node.js的API文档中得以见到还会有__filename、__dirname、module、exports多少个从未概念可是却存在的变量。此中__filename和__dirname在查找文件路线的进度中剖析得到后传入的。module变量是其一模块对象自己,exports是在module的布局函数中初叶化的二个空对象({},实际不是null)。
在此个主文件中,能够因而require方法去引进别的的模块。而实际那些require方法其实调用的正是module._load方法。
load方法在载入、编译、缓存了module后,再次回到module的exports对象。那就是circle.js文件中独有定义在exports对象上的法门手艺被表面调用的因由。

上述所汇报的模块载入机制均定义在lib/module.js中。

require 函数

require 引进的指标主假设函数。当 Node 调用 require(卡塔尔(قطر‎函数,况兼传递八个文本路线给它的时候,Node 会经验如下多少个步骤:

  • Resolving:找到文件的相对路线;
  • Loading:决断文件内容类型;
  • Wrapping:打包,给那个文件予以八个私家效用范围。那是使 require 和 module 模块在本地援用的黄金年代种方法;
  • Evaluating:VM 对加载的代码举办拍卖的地点;
  • Caching:当再一次索要用那几个文件的时候,无需重新一回上面步骤。


require.extensions 来查阅对两种文件的支撑情状

图片 1
可以清晰地收看 Node 对每一种扩大名所使用的函数及其操作:对 .js 文件使用 module._compile;对 .json 文件使用 JSON.parse;对 .node 文件使用 process.dlopen。

文件查找计策

  • 从文件模块缓存中加载

固然原生模块与公事模块的事前级不等,不过优先级最高的是从文件模块的缓存中加载已经存在的模块。

  • 从原生模块加载

原生模块的优先级紧跟于文件模块缓存的预先级。require方法在深入分析文件名之后,优先检查模块是还是不是在原生模块列表中。以http模块为例,尽管在目录下存在叁个http、http.js、http.node、http.json文件,require(“http”卡塔尔都不会从那些文件中加载,而是从原生模块中加载。
原生模块也许有一个缓存区,相似也是开始时期从缓存区加载。若是缓存区未有被加载过,则调用原生模块的加载方式进行加载和施行。

  • 从文件加载

当文件模块缓存中不设有,并且不是原生模块的时候,Node.js会深入深入分析require方法传入的参数,并从文件系统中加载实际的文书,加载进程中的包装和编写翻译细节在日前说过是调用load方法。

当 Node 遇到 require(X) 时,按下面的顺序处理。

(1)如果 X 是内置模块(比如 require('http')) 
  a. 返回该模块。 
  b. 不再继续执行。

(2)如果 X 以 "./" 或者 "/" 或者 "../" 开头 
  a. 根据 X 所在的父模块,确定 X 的绝对路径。 
  b. 将 X 当成文件,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。
        X
        X.js
        X.json
        X.node

  c. 将 X 当成目录,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。
        X/package.json(main字段)
        X/index.js
        X/index.json
        X/index.node

(3)如果 X 不带路径 
  a. 根据 X 所在的父模块,确定 X 可能的安装目录。 
  b. 依次在每个目录中,将 X 当成文件名或目录名加载。

(4) 抛出 "not found"

图片 2


模块循环信任

//创建两个文件,module1.js 和 module2.js,并且让它们相互引用
    // module1.js
    exports.a = 1;
    require('./module2');
    exports.b = 2;
    exports.c = 3;

    // module2.js
    const Module1 = require('./module1');
    console.log('Module1 is partially loaded here', Module1);

在 module1 完全加载在此之前须求先加载 module2,而 module2 的加载又须要module1。这种情状下,大家从 exports 对象中能获得的就是在发生循环信任在此以前的那有的。上边代码中,唯有 a 属性被引进,因为 b 和 c 都供给在引进 module2 之后技巧加载进来。

Node 使那一个主题素材轻便化,在多个模块加载时期开端创建 exports 对象。若是它须要引进别的模块,并且有轮重播重,那么只可以部分引进,也便是一定要引进发生循环信任在此以前所定义的那有的。

干什么要模块化

模块化这些话题在 ES6 早先是不设有的,因此这也被信口开河为前期 JavaScript 开采全局污染依赖管理混乱主题素材的根源。这类历史渊源和升高概述在本文将不会提起,由此感兴趣能够自行检索 JavaScript 发展史举行领会。

AMD

英特尔 是 Asynchronous Module Definition 的简单的称呼,即“异步模块定义”,是从 CommonJS 商量中诞生的。AMD优先照拂浏览器的模块加载场景,使用了异步加载和回调的方法。

英特尔 和 CommonJS 相通须要剧本加载器,即使 Intel 只须求对 define 方法的支持。define 方法需求四个参数:模块名称,模块运维的注重性数组,全部信任都可用之后试行的函数(该函数按照看重声明的次第,选取依赖作为参数)。唯有函数参数是务必的。define 既是生机勃勃种引用模块的办法,也是概念模块的办法。

// file lib/sayModule.js
define(function (){
    return {
        sayHello: function () {
            console.log('hello');
        }
    };
});

//file main.js
define(['./lib/sayModule'], function (say){
    say.sayHello(); //hello
})

main.js 作为任何应用的进口模块,我们利用 define 关键字表明了该模块以至外界重视(未有生命模块名称卡塔尔国;当大家实施该模块代码时,也正是实践define 函数的第贰个参数中定义的函数功能,其会在框架将富有的其余注重模块加载完结后被实践。这种延迟代码推行的本领也就确定保证了依靠的面世加载。

RequireJS

RequireJS 是三个前端的模块化处理的工具库,固守英特尔标准,通过贰个函数来将全数所须要的或然说所重视的模块达成装载进来,然后回到贰个新的函数(模块),咱们具备的有关新模块的业务代码都在此个函数内部操作,其内部也可无约束的行使已经加载进来的来讲的模块。

<script data-main='scripts/main' src='scripts/require.js'></script>
//scripts下的main.js则是指定的主代码脚本文件,所有的依赖模块代码文件都将从该文件开始异步加载进入执行。

defined用于定义模块,RequireJS需要各类模块均位居独立的文书之中。依照是不是有借助别的模块的事态分为独立模块和非独立模块。

1、独立模块 不依附其余模块。直接定义

define({
    methodOne: function (){},
    methodTwo: function (){}
});

//等价于

define(function (){
    return {
        methodOne: function (){},
        methodTwo: function (){}
    };
});

2、非独立模块,对别的模块有依附

define([ 'moduleOne', 'moduleTwo' ], function(mOne, mTwo){
    ...
});

//或者

define( function( require ){
    var mOne = require( 'moduleOne' ),
        mTwo = require( 'moduleTwo' );
    ...
});

如上代码, define中有依据模块数组的 和 未有依附模块数组用require加载 那二种概念模块,调用模块的措施合称为英特尔形式,定义模块清晰,不会污染全局变量,清楚的来得依赖关系。英特尔方式能够用来浏览器情境而且同意非同步加载模块,也得以按需动态加载模块。

在模块化那东西没出去早前,前端脚本引用差没有多少是这么的:
前端发展到前天,已经有那个模块化的方案,比方Intel、CMD、UMD、CommonJS等,当然了,还应该有es6推动的模块系统,那些模块化标准的主干价值都以让 JavaScript 的模块化开辟变得轻巧和自然,前几天就来看看这一个专门的学问都是什么。

直接步入正题,大家来拜会科学普及的模块化方案都有啥以致他们都有哪些内容。

CMD

CMD(Common Module Definition),在CMD中,叁个模块就是贰个文件。

全局函数define,用来定义模块。
参数 factory 能够是叁个函数,也可感觉对象恐怕字符串。
当 factory 为指标、字符串时,表示模块的接口正是该对象、字符串。

定义JSON数据模块:

define({ "foo": "bar" });

factory 为函数的时候,表示模块的构造方法,执行构造方法便可以得到模块向外提供的接口。

define( function(require, exports, module) { 
    // 模块代码
});

SeaJS

sea.js 宗旨特征:

1.坚守CMD标准,与NodeJS般的书写模块代码。
2.信赖自动加载,配置清晰简洁。
seajs.use用来在页面中加载三个要么四个模块

 // 加载一个模块 
seajs.use('./a');

// 加载模块,加载完成时执行回调
seajs.use('./a',function(a){
    a.doSomething();
});

// 加载多个模块执行回调
seajs.use(['./a','./b'],function(a , b){
    a.doSomething();
    b.doSomething();
});

英特尔和CMD最大的界别是对依靠模块的施行机缘管理分裂,注意不是加载的机缘可能措施各异。 超多少人说requireJS是异步加载模块,SeaJS是联合签字加载模块,这么清楚实际上是不纯粹的,其实加载模块都以异步的,只可是AMD注重前置,js能够低价清楚重视模块是何人,马上加载,而CMD就近重视,必要选拔把模块变为字符串深入剖析叁回才知晓注重了那么些模块,那也是累累人指责CMD的少数,捐躯质量来拉动开荒的便利性,实际上分析模块用的小运短到可以忽视。为什么正是施行时机管理分歧? 相通都是异步加载模块,AMD在加载模块产生后就能够实行该模块,全人体模型块都加载实践完后会进来回调函数,施行主逻辑,那样的效率便是依赖模块的试行各类和书写顺序不断定大器晚成致,看互连网速度,哪个先下载下来,哪个先进行,不过主逻辑一定在装有依赖加载成功后才施行。 CMD加载完某些信任模块后并不实践,只是下载而已,在具有依赖模块加载成功后步向主逻辑,碰到require语句的时候才实行相应的模块,那样模块的实行种种和书写顺序是完全黄金时代致的。

何以要模块化

  1. CommonJS

UMD

集结模块定义(UMD:Universal Module Definition )正是将 AMD 和 CommonJS 合在一块儿的后生可畏种尝试,习认为常的做法是将CommonJS 语法包裹在格外 英特尔 的代码中。

(function(define) {
    define(function () {
        return {
            sayHello: function () {
                console.log('hello');
            }
        };
    });
}(
    typeof module === 'object' && module.exports && typeof define !== 'function' ?
    function (factory) { module.exports = factory(); } :
    define
));

该形式的核激情想在于所谓的 IIFE(Immediately Invoked Function Expression),该函数会基于情状来推断需求的参数类别

在模块化那东西没出来此前,前端脚本引用大致是那般的:

CommonJS 的多个模块就是三个剧本文件,通过举行该文件来加载模块。CommonJS 标准规定,种种模块内部,module变量代表当前模块。那几个变量是多个目的,它的 exports 属性是对外的接口。加载有些模块,其实是加载该模块的module.exports属性。

ES6模块(module)

严格形式

ES6 的模块自动采纳严刻方式,不管有未有在模块底部加上"use strict";。
严峻形式主要有以下约束。

  • 变量必需注明后再选用
  • 函数的参数不可能有同名属性,不然报错
  • 不能够动用with语句
  • 不可能对只读属性赋值,不然报错
  • 不可能采取前缀0表示八进制数,不然报错
  • 不能够去除不可删除的习性,不然报错
  • 不能去除变量delete prop,会报错,只好删除属性delete global[prop]
  • eval不会在它的外层效率域引进变量
  • eval和arguments不能被另行赋值
  • arguments不会活动反映函数参数的变迁
  • 无法动用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局对象
  • 无法运用fn.caller和fn.arguments获取函数调用的库房
  • 日增了保留字(举个例子protected、static和interface)

模块Module

叁个模块,正是五个对其余模块拆穿自身的天性大概措施的公文。

导出Export

用作三个模块,它能够选用性地给任何模块暴光(提供)本身的性情和方法,供别的模块使用。

// profile.js
export var firstName = 'qiqi';
export var lastName = 'haobenben';
export var year = 1992;

//等价于

var firstName = 'qiqi';
var lastName = 'haobenben';
var year = 1992;
export {firstName, lastName, year}

1、 平日意况下,export输出的变量就是自然的名字,可是能够动用as关键字重命名。

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

//上面代码使用as关键字,重命名了函数v1和v2的对外接口。重命名后,v2可以用不同的名字输出两次
。

2、 必要特别注意的是,export命令规定的是对外的接口,必得与模块内部的变量创设梯次对应提到。

// 报错
export 1;

// 报错
var m = 1;
export m;

//上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出1,第二种写法通过变量m,还是直接输出1。1只是一个值,不是接口。

/ 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};

//上面三种写法都是正确的,规定了对外的接口m。其他脚本可以通过这个接口,取到值1。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。

3、最终,export命令能够出现在模块的别的岗位,只要处于模块顶层就足以。若是处在块级功效域内,就能够报错,接下来讲的import命令也是如此。

function foo() {
  export default 'bar' // SyntaxError
}
foo()

导入import

作为多个模块,能够依照必要,引进其余模块的提供的性质恐怕措施,供本人模块使用。

1、 import命令接受生机勃勃对大括号,里面钦赐要从别的模块导入的变量名。大括号里面的变量名,必得与被导入模块(profile.js)对外接口的名目豆蔻年华致。假设想为输入的变量重新取一个名字,import命令要采取as关键字,将输入的变量重命名。

import { lastName as surename } from './profile';

2、import后边的from内定模块文件的任务,能够是相对路线,也得以是相对路线,.js路线能够总结。假诺只是模块名,不带有路线,那么必得有布署文件,告诉 JavaScript 引擎该模块的岗位。

3、注意,import命令具有进步效果与利益,会升级到总人体模型块的底部,首先实行。

foo();

import { foo } from 'my_module';

//上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。

4、由于import是静态推行,所以不能够动用表达式和变量,那个唯有在运作时技艺获取结果的语法构造。

// 报错
import { 'f' + 'oo' } from 'my_module';

// 报错
let module = 'my_module';
import { foo } from module;

// 报错
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}

5、最终,import语句会执行所加载的模块,因而得以有下边包车型大巴写法。

import 'lodash';
//上面代码仅仅执行lodash模块,但是不输入任何值。

图片 3

大家见过那样的模块引用:

暗中同意导出(export defaultState of Qatar

各类模块扶植大家导出多个还未有名字的变量,使用首要语句export default来实现.

export default function(){
            console.log("I am default Fn");
        }
//使用export default关键字对外导出一个匿名函数,导入这个模块的时候,可以为这个匿名函数取任意的名字

//取任意名字均可
import sayDefault from "./module-B.js";
sayDefault();
//结果:I am default Fn

1、暗中认可输出和健康输出的可比

// 第一组
export default function diff() { // 输出
  // ...
}

import diff from 'diff'; // 输入

// 第二组
export function diff() { // 输出
  // ...
};

import {diff} from 'diff'; // 输入

//上面代码的两组写法,第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。

export default命令用于钦命模块的默许输出。显然,叁个模块只可以有八个私下认可输出,因而export default命令只可以利用贰回。所以,import命令后边才不用加大括号,因为只只怕对应多少个艺术。

2、因为export default本质是将该命令前边的值,赋给default变量以后再默许,所以直接将一个值写在export default未来。

// 正确
export default 42;

// 报错
export 42;

//上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定外对接口为default。

3、假如想在一条import语句中,同不时候输入默许方法和任何变量,能够写成下边那样。

import _, { each } from 'lodash';

//对应上面代码的export语句如下
export default function (){
    //...
}
export function each (obj, iterator, context){
    //...
}

模块把接口暴露到全局对象下(比方window),各样模块能够通过全局对象访谈各种重视的接口,不过也设有有的标题:

var myModule = require('module');myModule.sayHello();

export 与 import 的复合写法

假使在叁个模块之中,先输入后输出同三个模块,import语句能够与export语句写在一同。

export { foo, bar } from 'my_module';

// 等同于
import { foo, bar } from 'my_module';
export { foo, bar };

/ 接口改名
export { foo as myFoo } from 'my_module';

// 整体输出
export * from 'my_module';

注意事项 1、注明的变量,对外都是只读的。然而导出的是指标类型的值,就可校订。 2、导入不设有的变量,值为undefined。

ES6 中的循环援用

ES6 中,imports 是 exprts 的只读视图,直白一点就是,imports 都针对 exports 原来的多寡,比方:

//------ lib.js ------
export let counter = 3;
export function incCounter() {
    counter++;
}

//------ main.js ------
import { counter, incCounter } from './lib';

// The imported value `counter` is live
console.log(counter); // 3
incCounter();
console.log(counter); // 4

// The imported value can’t be changed
counter++; // TypeError

之所以在 ES6 中拍卖循环援用非常轻易,看下边这段代码:

//------ a.js ------
import {bar} from 'b'; // (1)
export function foo() {
  bar(); // (2)
}

//------ b.js ------
import {foo} from 'a'; // (3)
export function bar() {
  if (Math.random()) {
    foo(); // (4)
  }
}

风姿潇洒旦先加载模块 a,在模块 a 加载成功将来,bar 直接性地指向的是模块 b 中的 bar。无论是加载成功的 imports 依旧未成功的 imports,imports 和 exports 之间都有二个直接的关系,所以一而再三回九转可以寻常干活。

实例

//---module-B.js文件---
//导出变量:name
export var name = "cfangxu";

moduleA模块代码:
//导入 模块B的属性 name    
import { name } from "./module-B.js";   
console.log(name)
//打印结果:cfangxu

批量导出

//属性name
var name = "cfangxu";
//属性age
var age  = 26;
//方法 say
var say = function(){
            console.log("say hello");
         }
//批量导出
export {name,age,say}

批量导入

//导入 模块B的属性
import { name,age,say } from "./module-B.js";
console.log(name)
//打印结果:cfangxu
console.log(age)
//打印结果:26
say()
//打印结果:say hello

重命名导入变量

import {name as myName} from './module-B.js';
console.log(myName) //cfangxu
整体导入

/使用*实现整体导入
import * as obj from "./module-B.js";

console.log(obj.name)
//结果:"cfangxu"
console.log(obj.age)
//结果:26
obj.say();
//结果:say hello

1、挂在大局对象下轻松生出冲突
2、种种脚本加载的必需从严遵照望重顺序,不然恐怕就玩不转
3、在多个特意大的门类中,引用的台本就会非常多,script 标签就能够特地多,並且难以维护。比方:

这是因为大家把模块的措施定义在了模块的属性上:

推荐材料

  • JavaSript模块典型 - 英特尔标准与CMD标准介绍
  • JavaScript 模块演变简史
  • require()源码解读
  • 在 Node.js 中引进模块:你所必要领会的一切都在那
  • 深入浅出Node.js(三):深刻Node.js的模块机制

图片 4

// module.jsmodule.exports.sayHello = function() { console.log('Hello ');};// 如果这样写module.exports = sayHello;// 调用则需要改为var sayHello = require('module');sayHello();

介绍

模块平常是指编制程序语言商讨所提供的代码组织机制,利用此机制可将次第拆解为单独且通用的代码单元。所谓模块化首借使缓和代码分割、成效域隔开、模块之间的依赖管理以致公布到临蓐条件时的自动化打包与拍卖等八个地点。

模块的帮助和益处

1.可维护性。 因为模块是单身的,二个设计精美的模块会让外部的代码对协调的信任性越少越好,这样和和气气就足以单独去修改和修正。
2.命名空中。 在 JavaScript 里面,若是叁个变量在最一流的函数之外注明,它就一贯成为全局可用。因而,经常相当大心现身命名冲突的情景。使用模块化开垦来封装变量,能够幸免污染全局碰着。
3.重用代码。 大家有时会欣赏早先面写过的项目中拷贝代码到新的品种,那并没反常,可是更加好的办法是,通过模块援用的办法,来制止再一次的代码库。

为了减轻这个主题材料,各个模块化的方案都出来了

require命令第一遍加载该脚本时就能够进行总体脚本,然后在内部存款和储蓄器中生成贰个指标,那么些结果长成那样:

CommonJS

CommonJS 最开始是 Mozilla 的程序员于 2009年始发的二个类型,它的目标是让浏览器之外的 JavaScript (比如服务器端只怕桌面端)能够透过模块化的不二诀要来开辟和搭档。

在 CommonJS 的标准中,每一个 JavaScript 文件就是二个单身的模块上下文(module context),在这里个上下文中暗许创建的品质都以私家的。也正是说,在叁个文书定义的变量(还包涵函数和类),都以私有的,对别的文件是不可以知道的。

急需留意的是,CommonJS 规范的基本点适用途景是劳动器端编制程序,所以采纳一块加载模块的主旨。假如大家依赖3个模块,代码会二个三个梯次加载它们。

该模块完毕方案主要含有 require 与 module 那八个根本字,其同意有些模块对外暴光部分接口并且由其它模块导入使用。

//sayModule.js
function SayModule () {
    this.hello = function () {
        console.log('hello');
    };

    this.goodbye = function () {
        console.log('goodbye');
    };
}

module.exports = SayModule;

//main.js 引入sayModule.js
var Say = require('./sayModule.js');
var sayer = new Say();
sayer.hello(); //hello

用作一个服务器端的消除方案,CommonJS 须求多个相配的剧本加载器作为前提条件。该脚本加载器必得帮衬名称为 require 和 module.exports 的函数,它们将模块互相导入导出。

Node.js

Node 从 CommonJS 的有的创新意识中,创设出自个儿的模块化达成。由于Node 在服务端的流行,Node 的模块方式被(不科学地)称为 CommonJS。

Node.js模块能够分为两大类,风流倜傥类是骨干模块,另后生可畏类是文件模块。
主导模块 正是Node.js规范的API中提供的模块,如fs、http、net等,这一个都以由Node.js官方提供的模块,编写翻译成了二进制代码,能够直接通过require获取基本模块,譬喻require('fs'卡塔尔国,宗旨模块具备最高的加载优先级,假使有模块与主导模块命名冲突,Node.js总是会加载大旨模块。
文件模块 是存款和储蓄为单独的公文(或文件夹)的模块,恐怕是JavaScript代码、JSON或编写翻译好的C/C++代码。在不显式钦点文件模块扩张名的时候,Node.js会分别总括加上.js、.json、.node(编写翻译好的C/C++代码卡塔尔(قطر‎。

加载情势

  • 按路线加载模块

假使require参数风流倜傥"/"初步,那么就以相对路线的措施查找模块名称,如若参数后生可畏"./"、"../"发轫,那么则是以相对路线的不二等秘书诀来查找模块。

  • 经过寻觅node_modules目录加载模块

只要require参数不以"/"、"./"、"../"开始,而该模块又不是骨干模块,那么快要通过寻觅node_modules加载模块了。我们应用的npm获取的包常常正是以这种措施加载的。

加载缓存

Node.js模块不会被重新加载,这是因为Node.js通过文件名缓存全数加载过的文本模块,所以随后再拜望届时就不会重复加载了。
小心: Node.js是基于实际文件名缓存的,实际不是require(State of Qatar提供的参数缓存的,也便是说尽管你分别通过require('express'卡塔尔国和require('./node_modules/express'State of Qatar加载三回,也不会重新加载,因为固然四回参数区别,解析到的公文却是同二个。

Node.js 中的模块在加载之后是以单例化运转,何况依照值传递原则:假若是贰个对象,就一定于这么些目的的引用。

模块载入进度

加载文件模块的办事,主要由原生模块module来落到实处和产生,该原生模块在运行时已经被加载,进度向来调用到runMain静态方法。

例如运行: node app.js

Module.runMain = function () {
    // Load the main module--the command line argument.
    Module._load(process.argv[1], null, true);
};

//_load静态方法在分析文件名之后执行
var module = new Module(id, parent);

//并根据文件路径缓存当前模块对象,该模块实例对象则根据文件名加载。
module.load(filename);

切切实实说一下上文提到了文本模块的三类模块,那三类文件模块然后缀来差异,Node.js会根据后缀名来调节加载方法,具体的加载方法在下文require.extensions中会介绍。

  • .js 通过fs模块同步读取js文件并编写翻译施行。
  • .node 通过C/C++进行编辑的Addon。通过dlopen方法开展加载。
  • .json 读取文件,调用JSON.parse解析加载。

接下去详细描述js后缀的编写翻译进度。Node.js在编写翻译js文件的进度中实际上完结的手续有对js文件内容进行头尾包装。以app.js为例,包装之后的app.js将会化为以下方式:

//circle.js
var PI = Math.PI;
exports.area = function (r) {
    return PI * r * r;
};
exports.circumference = function (r) {
    return 2 * PI * r;
};

//app.js
var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is ' + circle.area(4));

//app包装后
(function (exports, require, module, __filename, __dirname) {
    var circle = require('./circle.js');
    console.log('The area of a circle of radius 4 is ' + circle.area(4));
});

//这段代码会通过vm原生模块的runInThisContext方法执行(类似eval,只是具有明确上下文,不污染全局),返回为一个具体的function对象。最后传入module对象的exports,require方法,module,文件名,目录名作为实参并执行。

那正是为什么require并未有概念在app.js 文件中,可是这么些艺术却存在的由来。从Node.js的API文书档案中得以阅览还会有__filename、__dirname、module、exports多少个未有概念但是却存在的变量。当中__filename和__dirname在查找文件路线的历程中深入分析拿到后传入的。module变量是以此模块对象自己,exports是在module的构造函数中初阶化的三个空对象({},实际不是null)。
在这里个主文件中,能够透过require方法去引进别的的模块。而实际上那一个require方法其实调用的正是module._load方法。
load方法在载入、编写翻译、缓存了module后,重回module的exports对象。那正是circle.js文件中只有定义在exports对象上的不二法门技巧被表面调用的案由。

如上所描述的模块载入机制均定义在lib/module.js中。

require 函数

require 引进的靶子首若是函数。当 Node 调用 require()函数,何况传递一个文书路线给它的时候,Node 会经历如下多少个步骤:

  • Resolving:找到文件的相对路径;
  • Loading:判定文件内容类型;
  • Wrapping:打包,给那个文件予以多少个民用作用范围。那是使 require 和 module 模块在本地援用的少年老成种格局;
  • Evaluating:VM 对加载的代码实行拍卖的地点;
  • Caching:当再度索要用这么些文件的时候,没有必要再一次三遍下面步骤。


require.extensions 来查阅对三种文件的扶助情况

图片 5
能够清楚地察看 Node 对各类扩充名所使用的函数及其操作:对 .js 文件使用 module._compile;对 .json 文件使用 JSON.parse;对 .node 文件使用 process.dlopen。

文件查找战术

  • 从文件模块缓存中加载

即使原生模块与公事模块的初期级不等,不过优先级最高的是从文件模块的缓存中加载已经存在的模块。

  • 从原生模块加载

原生模块的优先级稍差于文件模块缓存的预先级。require方法在分条析理文件名今后,优先检查模块是或不是在原生模块列表中。以http模块为例,就算在目录下存在叁个http、http.js、http.node、http.json文件,require(“http”State of Qatar都不会从那么些文件中加载,而是从原生模块中加载。
原生模块也可能有一个缓存区,相像也是预先从缓存区加载。尽管缓存区未有被加载过,则调用原生模块的加载方式开展加载和实行。

  • 从文件加载

当文件模块缓存中不设有,何况不是原生模块的时候,Node.js会深入深入分析require方法传入的参数,并从文件系统中加载实际的公文,加载进度中的包装和编写翻译细节在前方说过是调用load方法。

当 Node 遇到 require(X) 时,按下面的顺序处理。

(1)如果 X 是内置模块(比如 require('http')) 
  a. 返回该模块。 
  b. 不再继续执行。

(2)如果 X 以 "./" 或者 "/" 或者 "../" 开头 
  a. 根据 X 所在的父模块,确定 X 的绝对路径。 
  b. 将 X 当成文件,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。
        X
        X.js
        X.json
        X.node

  c. 将 X 当成目录,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。
        X/package.json(main字段)
        X/index.js
        X/index.json
        X/index.node

(3)如果 X 不带路径 
  a. 根据 X 所在的父模块,确定 X 可能的安装目录。 
  b. 依次在每个目录中,将 X 当成文件名或目录名加载。

(4) 抛出 "not found"

图片 6


模块循环信任

//创建两个文件,module1.js 和 module2.js,并且让它们相互引用
    // module1.js
    exports.a = 1;
    require('./module2');
    exports.b = 2;
    exports.c = 3;

    // module2.js
    const Module1 = require('./module1');
    console.log('Module1 is partially loaded here', Module1);

在 module1 完全加载早先须要先加载 module2,而 module2 的加载又需求module1。这种场合下,我们从 exports 对象中能拿到的正是在发生循环正视以前的那大器晚成都部队分。上边代码中,独有 a 属性被引进,因为 b 和 c 都亟需在引进 module2 之后本事加载进来。

Node 使这么些难点轻便化,在三个模块加载时期最早创办 exports 对象。就算它需求引进别的模块,况兼有轮回信赖,那么只可以部分引进,相当于只可以引进爆发循环注重早先所定义的这豆蔻梢头部分。

CommonJS

{ id: '...', exports: { ... }, loaded: true, ...}

AMD

AMD 是 Asynchronous Module Definition 的简单的称呼,即“异步模块定义”,是从 CommonJS 钻探中出生的。Intel优先照看浏览器的模块加载场景,使用了异步加载和回调的艺术。

英特尔 和 CommonJS 同样供给剧本加载器,固然 英特尔 只供给对 define 方法的匡助。define 方法必要多少个参数:模块名称,模块运转的依据数组,全数信赖都可用之后实施的函数(该函数依照信赖评释的风度翩翩后生可畏,采取依赖作为参数)。唯有函数参数是必需的。define 既是后生可畏种引用模块的格局,也是概念模块的方法。

// file lib/sayModule.js
define(function (){
    return {
        sayHello: function () {
            console.log('hello');
        }
    };
});

//file main.js
define(['./lib/sayModule'], function (say){
    say.sayHello(); //hello
})

main.js 作为任何应用的进口模块,大家利用 define 关键字注明了该模块甚至外部重视(未有生命模块名称State of Qatar;当我们推行该模块代码时,相当于实施define 函数的第三个参数中定义的函数功效,其会在框架将全数的别的信任模块加载完成后被实行。这种延迟代码实施的技巧也就保险了依靠的面世加载。

RequireJS

RequireJS 是八个前端的模块化管理的工具库,死守英特尔典型,通过叁个函数来将全体所须求的或然说所依赖的模块完毕装载进来,然后回到叁个新的函数(模块),大家具有的关于新模块的作业代码都在这里个函数内部操作,其里面也可Infiniti定的利用已经加载进来的来说的模块。

<script data-main='scripts/main' src='scripts/require.js'></script>
//scripts下的main.js则是指定的主代码脚本文件,所有的依赖模块代码文件都将从该文件开始异步加载进入执行。

defined用于定义模块,RequireJS供给各种模块均坐落于独立的文件之中。依照是不是有依赖其余模块的情事分为独立模块和非独立模块。

1、独立模块 不重视其余模块。间接定义

define({
    methodOne: function (){},
    methodTwo: function (){}
});

//等价于

define(function (){
    return {
        methodOne: function (){},
        methodTwo: function (){}
    };
});

2、非独立模块,对此外模块有依附

define([ 'moduleOne', 'moduleTwo' ], function(mOne, mTwo){
    ...
});

//或者

define( function( require ){
    var mOne = require( 'moduleOne' ),
        mTwo = require( 'moduleTwo' );
    ...
});

如上代码, define中有依据模块数组的 和 未有信赖模块数组用require加载 那三种概念模块,调用模块的主意合称为Intel情势,定义模块清晰,不会传染全局变量,清楚的显得依赖关系。英特尔方式能够用于浏览器境况並且同意非同步加载模块,也可以按需动态加载模块。

这种措施通过二个称得上require的点子,同步加载依赖,然后返导出API供别的模块使用,一个模块能够通过exports大概module.exports导出API。CommonJS规范中,叁个单独的文书就是一个模块。每一个模块都是三个独立的功效域,在八个文本中定义的变量,都以个体的,对其它文件是不可以预知的。

Node.js 的模块机制贯彻正是参照了 CommonJS 的规范。不过 Node.js 额外做了意气风发件事,即为种种模块提供了一个 exports 变量,以指向 module.exports,这一定于在种种模块最在这里从前,写有这么生机勃勃行代码:

CMD

CMD(Common Module Definition),在CMD中,三个模块正是一个文件。

全局函数define,用来定义模块。
参数 factory 能够是一个函数,也足以为对象或许字符串。
当 factory 为目的、字符串时,表示模块的接口正是该对象、字符串。

定义JSON数据模块:

define({ "foo": "bar" });

factory 为函数的时候,表示模块的构造方法,执行构造方法便可以得到模块向外提供的接口。

define( function(require, exports, module) { 
    // 模块代码
});

SeaJS

sea.js 核心特征:

1.据守CMD标准,与NodeJS般的书写模块代码。
2.依据自动加载,配置清晰简洁。
seajs.use用来在页面中加载二个也许八个模块

 // 加载一个模块 
seajs.use('./a');

// 加载模块,加载完成时执行回调
seajs.use('./a',function(a){
    a.doSomething();
});

// 加载多个模块执行回调
seajs.use(['./a','./b'],function(a , b){
    a.doSomething();
    b.doSomething();
});

英特尔和CMD最大的区分是对注重模块的施行机遇管理不一致,注意不是加载的火候大概措施分化。 很几人说requireJS是异步加载模块,SeaJS是同步加载模块,这么清楚实际上是不确切的,其实加载模块都是异步的,只可是英特尔正视后置,js能够方便明白信任模块是什么人,登时加载,而CMD就近信赖,必要选取把模块变为字符串深入分析二次才领会信赖了那个模块,那也是许五个人非议CMD的一点,就义品质来推动开拓的便利性,实际上解析模块用的时刻短到能够忽视。为啥便是实践机会管理分裂? 相仿都是异步加载模块,AMD在加载模块变成后就能实施该模块,全部模块都加载试行完后会进去回调函数,实施主逻辑,那样的功力正是依靠模块的实施各类和书写顺序不鲜明后生可畏致,看网络速度,哪个先下载下来,哪个先实行,不过主逻辑一定在富有正视加载成功后才实践。 CMD加载完某些信任模块后并不实行,只是下载而已,在有着信任模块加载成功后步入主逻辑,碰着require语句的时候才实践相应的模块,那样模块的实施顺序和书写顺序是完全一致的。

图片 7

var exports = module.exports;

UMD

合并模块定义(UMD:Universal Module Definition )就是将 英特尔 和 CommonJS 合在联合的生龙活虎种尝试,不足为奇的做法是将CommonJS 语法包裹在非常 英特尔 的代码中。

(function(define) {
    define(function () {
        return {
            sayHello: function () {
                console.log('hello');
            }
        };
    });
}(
    typeof module === 'object' && module.exports && typeof define !== 'function' ?
    function (factory) { module.exports = factory(); } :
    define
));

该方式的核心理想在于所谓的 IIFE(Immediately Invoked Function Expression),该函数会基于情形来剖断供给的参数连串

服务端Node.js就是用的这种方法。

CommonJS 模块的本性:

ES6模块(module)

适度从紧格局

ES6 的模块自动接受严俊情势,不管有未有在模块尾部加上"use strict";。
严加情势首要有以下约束。

  • 变量必得申明后再利用
  • 函数的参数不可能有同名属性,不然报错
  • 不能够选择with语句
  • 不能够对只读属性赋值,不然报错
  • 不能够应用前缀0表示八进制数,不然报错
  • 不能去除不可删除的习性,不然报错
  • 不可能去除变量delete prop,会报错,只可以删除属性delete global[prop]
  • eval不会在它的外层功用域引进变量
  • eval和arguments不可能被再一次赋值
  • arguments不会自动反映函数参数的更动
  • 不能够采取arguments.callee
  • 无法动用arguments.caller
  • 不允许this指向全局对象
  • 不能够选用fn.caller和fn.arguments获取函数调用的饭馆
  • 日增了保留字(比方protected、static和interface)

模块Module

四个模块,便是一个对其他模块揭露本身的属性可能措施的文件。

导出Export

用作三个模块,它还不错性地给其余模块暴光(提供)自身的属性和措施,供其余模块使用。

// profile.js
export var firstName = 'qiqi';
export var lastName = 'haobenben';
export var year = 1992;

//等价于

var firstName = 'qiqi';
var lastName = 'haobenben';
var year = 1992;
export {firstName, lastName, year}

1、 经常状态下,export输出的变量正是本来的名字,但是能够利用as关键字重命名。

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

//上面代码使用as关键字,重命名了函数v1和v2的对外接口。重命名后,v2可以用不同的名字输出两次
。

2、 必要特别注意的是,export命令规定的是对外的接口,必需与模块内部的变量建立梯次对应涉及。

// 报错
export 1;

// 报错
var m = 1;
export m;

//上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出1,第二种写法通过变量m,还是直接输出1。1只是一个值,不是接口。

/ 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};

//上面三种写法都是正确的,规定了对外的接口m。其他脚本可以通过这个接口,取到值1。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。

3、最终,export命令能够出现在模块的别的岗位,只要处于模块顶层就足以。要是处在块级作用域内,就能够报错,接下来讲的import命令也是那般。

function foo() {
  export default 'bar' // SyntaxError
}
foo()

导入import

作为多少个模块,能够依附须求,引进别的模块的提供的性质或许措施,供自个儿模块使用。

1、 import命令选择风度翩翩对大括号,里面钦定要从此外模块导入的变量名。大括号里面包车型地铁变量名,必得与被导入模块(profile.js)对外接口的称号风流倜傥致。假如想为输入的变量重新取贰个名字,import命令要选取as关键字,将输入的变量重命名。

import { lastName as surename } from './profile';

2、import前边的from钦点模块文件的职责,能够是相对路线,也能够是相对路线,.js路线能够回顾。如若只是模块名,不带有路线,那么必需有配备文件,告诉 JavaScript 引擎该模块的任务。

3、注意,import命令具备进步效果与利益,会升级到全方位模块的头顶,首先实践。

foo();

import { foo } from 'my_module';

//上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。

4、由于import是静态推行,所以无法选拔表明式和变量,那么些独有在运转时才具获得结果的语法布局。

// 报错
import { 'f' + 'oo' } from 'my_module';

// 报错
let module = 'my_module';
import { foo } from module;

// 报错
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}

5、最终,import语句会实施所加载的模块,由此得以有上面包车型大巴写法。

import 'lodash';
//上面代码仅仅执行lodash模块,但是不输入任何值。

Well

抱有代码都运营在模块功用域,不会传染全局成效域。独立性是模块的重中之重特点就,模块内部最佳不与程序的其他部分直接相互作用。模块可以频仍加载,可是只会在率先次加载时运行叁回,然后运营结果就被缓存了,未来再加载,就一向读取缓存结果。要想让模块再度运转,必须消除缓存。模块加载的相继,依照其在代码中现身的逐生机勃勃。2. 英特尔

私下认可导出(export default卡塔尔(قطر‎

每种模块扶植大家导出叁个从没有过名字的变量,使用首要语句export default来落成.

export default function(){
            console.log("I am default Fn");
        }
//使用export default关键字对外导出一个匿名函数,导入这个模块的时候,可以为这个匿名函数取任意的名字

//取任意名字均可
import sayDefault from "./module-B.js";
sayDefault();
//结果:I am default Fn

1、默许输出和健康输出的比较

// 第一组
export default function diff() { // 输出
  // ...
}

import diff from 'diff'; // 输入

// 第二组
export function diff() { // 输出
  // ...
};

import {diff} from 'diff'; // 输入

//上面代码的两组写法,第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。

export default命令用于内定模块的默许输出。显明,三个模块只好有七个暗中认可输出,因而export default命令只可以采纳三次。所以,import命令前边才不用加大括号,因为只大概对应贰个艺术。

2、因为export default本质是将该命令后边的值,赋给default变量现在再暗许,所以一贯将四个值写在export default现在。

// 正确
export default 42;

// 报错
export 42;

//上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定外对接口为default。

3、要是想在一条import语句中,同一时间输入私下认可方法和任何变量,能够写成上边那样。

import _, { each } from 'lodash';

//对应上面代码的export语句如下
export default function (){
    //...
}
export function each (obj, iterator, context){
    //...
}

1、服务端模块能够很好的复用
2、这种作风的模块已经重重了,例如npm上海南大学学都都是这种风格的module
3、轻便易用

CommonJS 标准很好,然而不适用于浏览器情形,于是有了 Intel 和 CMD 三种方案。英特尔 全称 Asynchronous Module Definition,即异步模块定义。它选用异步方式加载模块,模块的加载不影响它背后语句的运作。全数信任这些模块的话语,都定义在叁个回调函数中,等到加载成功今后,那么些回调函数才会运作。除了和 CommonJS 同步加载方式不相同之外,英特尔 在模块的定义与援用上也天渊之别。

export 与 import 的复合写法

只要在二个模块之中,先输入后输出同叁个模块,import语句能够与export语句写在一块。

export { foo, bar } from 'my_module';

// 等同于
import { foo, bar } from 'my_module';
export { foo, bar };

/ 接口改名
export { foo as myFoo } from 'my_module';

// 整体输出
export * from 'my_module';

注意事项 1、注脚的变量,对外都以只读的。不过导出的是指标类型的值,就可纠正。 2、导入一纸空文的变量,值为undefined。

ES6 中的循环援用

ES6 中,imports 是 exprts 的只读视图,直白一点便是,imports 都照准exports 原来的数额,举个例子:

//------ lib.js ------
export let counter = 3;
export function incCounter() {
    counter++;
}

//------ main.js ------
import { counter, incCounter } from './lib';

// The imported value `counter` is live
console.log(counter); // 3
incCounter();
console.log(counter); // 4

// The imported value can’t be changed
counter++; // TypeError

由此在 ES6 中管理循环引用极其简单,看上边这段代码:

//------ a.js ------
import {bar} from 'b'; // (1)
export function foo() {
  bar(); // (2)
}

//------ b.js ------
import {foo} from 'a'; // (3)
export function bar() {
  if (Math.random()) {
    foo(); // (4)
  }
}

如果先加载模块 a,在模块 a 加载成功之后,bar 直接性地针没有错是模块 b 中的 bar。无论是加载成功的 imports 依然未到位的 imports,imports 和 exports 之间都有二个间接的牵连,所以总是能够健康办事。

实例

//---module-B.js文件---
//导出变量:name
export var name = "cfangxu";

moduleA模块代码:
//导入 模块B的属性 name    
import { name } from "./module-B.js";   
console.log(name)
//打印结果:cfangxu

批量导出

//属性name
var name = "cfangxu";
//属性age
var age  = 26;
//方法 say
var say = function(){
            console.log("say hello");
         }
//批量导出
export {name,age,say}

批量导入

//导入 模块B的属性
import { name,age,say } from "./module-B.js";
console.log(name)
//打印结果:cfangxu
console.log(age)
//打印结果:26
say()
//打印结果:say hello

重命名导入变量

import {name as myName} from './module-B.js';
console.log(myName) //cfangxu
整体导入

/使用*实现整体导入
import * as obj from "./module-B.js";

console.log(obj.name)
//结果:"cfangxu"
console.log(obj.age)
//结果:26
obj.say();
//结果:say hello

Less Well

define(id?, dependencies?, factory);

推荐介绍材质

  • JavaSript模块规范 - Intel标准与CMD标准介绍
  • JavaScript 模块演化简史
  • require(State of Qatar源码解读
  • 在 Node.js 中引进模块:你所急需知道的一切都在此
  • 初始Node.js(三):深刻Node.js的模块机制

原来的文章链接:

1、加载模块是一路的,所以唯有加载成功技能推行前边的操作
2、七个模块不能相互加载

英特尔 的模块引进由 define 方法来定义,在 define API 中:

像Node.js首要用来服务器的编制程序,加载的模块文件常常都曾经存在本地硬盘,所以加载起来非常快,不用构思异步加载的艺术,所以CommonJS标准相比较适用。但倘假如浏览器碰到,要从服务器加载模块,那是就非得接纳异步格局。所以就有了 AMD 、CMD 的搞定方案。

id:模块名称,恐怕模块加载器央求的内定脚本的名字;dependencies:是个概念中模块所信赖模块的数组,默以为 [“require”, “exports”, “module”],举个例证比较好领会,当大家创制一个名称为 “alpha” 的模块,使用了require,exports,和名字为 “beta” 的模块,需求如下书写;factory:为模块最初化要推行的函数或对象。借使为函数,它应当只被实施一次。假若是指标,此目的应当为模块的输出值;

AMD: 异步require
AMD === Asynchronous Module Definition

// 示例1define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); // 或者 return require("beta").verb(); }});

介于上边的提起的主题素材,于是浏览器端的异步版本的require应运而生

大器晚成旦模块定义不设有依赖,那么能够直接定义对象:

图片 8

define({ add: function(x, y){ return x + y; }});

英特尔 标准中,define 函数有七个国有属性 define.amd。

而使用时我们照旧通过 require 关键字,它包蕴七个参数,首个数组为要加载的模块,第4个参数为回调函数:

Well

require([module], callback);

1、消灭了模块异步加载的难点
2、消亡了多少个剧本并行加载的难题

举个例证:

Less Well

require(['math'], function (math) { math.add(2, 3);});

1、代码太过肥胖,相当不够尊贵,难以阅读和书写
2、可是就像又是某种建设方案

  1. CMD

Intel被选择的最斗的贯彻方案属实正是 require.js 了

CMD 全称为 Common Module Definition,是 Sea.js 所推广的多个模块化方案的出口。在 CMD define 的入参中,就算也帮忙富含 id, deps 以至 factory 多少个参数的款型,但推荐的是接收 factory 贰个入参,然后在入参实践时,填入四个参数 require、exports 和 module:

CMD表示不性格很顽强在荆棘满途或巨大压力面前不屈

define(function(require, exports, module) { var a = require('./a'); a.doSomething(); var b = require('./b'); b.doSomething(); ...})

CMD是SeaJS 在加大进度中对模块定义的标准化产出

因而实施该布局方法,能够赢得模块向外提供的接口。在与 Intel相比上设有四个重大的分裂点:

CMD 标准中定义了 define 函数有三个国有属性 define.cmd。

对于借助的模块,AMD 是提前实行,CMD 是延迟实施。但是 RequireJS 从 2.0 早先,也改成能够顺延实行。CMD 弘扬 as lazy as possible.CMD 弘扬正视就近,AMD 弘扬注重前置。

CMD 模块中有二种方法提供对外的接口,生机勃勃种是 exports.MyModule = ...,风华正茂种是选择 return 进行再次回到。

即便说的不精通,那么大家平素看上边的代码用 AMD 该怎么写:

CMD和AMD的区分有以下几点:

define(['./a', './b'], function(a, b) { a.doSomething(); b.doSomething(); ...})

1、对于依靠的模块英特尔是提前实行,CMD是延迟施行。(require2.0近似有生成)

  1. UMD

2、CMD弘扬重视就近,AMD推崇正视前置。

UMD,全称 Universal Module Definition,即通用模块标准。既然 CommonJs 和 英特尔风格大同小异流行,那么须求三个得以统豆蔻年华浏览器端以至非浏览器端的模块化方案的正经。

图片 9

直接来探视官方给出的 jQuery 模块怎么样用 UMD 定义的代码:

虽说Intel也扶持CMD写法,但依附前置是官方文书档案的暗中认可模块定义写法。
引进生机勃勃篇作品:SeaJS与RequireJS最大的分别

(function (factory) { if (typeof define === 'function'  define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object'  module.exports) { // Node/CommonJS module.exports = function( root, jQuery ) { if ( jQuery === undefined ) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } factory(jQuery); return jQuery; }; } else { // Browser globals factory(jQuery); }}(function ($) { $.fn.jqueryPlugin = function () { return true; };}));

UMD: 通用模块规范

UMD的落到实处很简短:

UMD是英特尔和CommonJS两个的组合,英特尔浏览器第生机勃勃的基准发展,异步加载模块。CommonJS 模块以服务器第意气风发规范进步,同步加载模块,它的模块不需求包装。

先决断是不是协理 AMD,存在则选用 英特尔 形式加载模块;再判定是不是扶持 Node.js 模块格式,存在则动用 Node.js 模块格式;前三个都不设有,则将模块公开到全局;5. ES Modules

只是本身意气风发旦想同一时候援救二种风格吗?于是通用模块规范(UMD)诞生了。这几个方式中到场了当前设有哪类标准的推断,所以能够“通用”,它非常了Intel和CommonJS,同一时候还帮助老式的“全局”变量标准:

不可否认,以上说的种种都是社区提供的方案,历史上,JavaScript 一向未曾模块系统,直到 ES6 在语言专门的学问的局面上,实现了它。其设计观念是硬着头皮的静态化,使得编写翻译时就能够明确模块的重视性关系,以致输入和出口的变量。CommonJS 和 英特尔 模块,都不能不在运作时规定这么些东西。例如,CommonJS 模块正是目的,输入时必得搜索对象属性。而 ES Modules 不是目的,而是通过export命令显式钦点输出的代码。

[图片上传中。。。(6)]

ES Modules 的模块化技术由export和import组成,export命令用于规定模块的对外接口,import命令用于输入任何模块提供的功效。大家可以这么定义一个模块:

ES6 modules: 你们都让开,小编才是标准

// 第一种方式export var firstName = 'Michael';export var lastName = 'Jackson';export var year = 1958;// 第二种方式var firstName = 'Michael';var lastName = 'Jackson';var year = 1958;export { firstName, lastName, year };

ES6为JavaScript增加了有个别言语布局,形成另叁个模块系统。

然后再如此引进他们:

[图片上传中。。。(7)]

本文由杏彩发布,转载请注明来源

关键词: