本系列属于阮一峰老师所著的ECMAScript 6 入门学习笔记javascript
在ES6以前,JavaScript一直没有模块(module)体系,没法将一个大程序拆分红互相依赖的小文件,再用简单的方法拼装起来。社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。java
ES6在语言标准的层面上,实现了模块功能,并且实现很简单,能够彻底取代CommmJS和AMD规范,成为浏览器和服务器通用的模块解决方案。es6
因为 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,好比引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。浏览器
ES6 的模块自动采用严格模式,无论你有没有在模块头部加上"use strict";
。服务器
严格模式主要有如下限制。异步
with
语句delete prop
,会报错,只能删除属性delete global[prop]
eval
不会在它的外层做用域引入变量eval
和arguments
不能被从新赋值arguments
不会自动反映函数参数的变化arguments.callee
arguments.caller
this
指向全局对象fn.caller
和fn.arguments
获取函数调用的堆栈protected
、static
和interface
)模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其余模块提供的功能。函数
一个模块就是一个独立的文件。该文件内部的全部变量,外部没法获取。学习
// 对外输出变量 export var firstName = 'Angus' export var lastName = 'jay' export var year = 1998 // 另外一种写法 var firstName = 'Angus' var lastName = 'jay' var year = 1998 export {firstName,lastName,year} // 优先考虑这种写法。能够在脚本尾部清除看出输出变量 // 除了输出变量,还能够输出函数或类(class) export function multiply(x,y){ retrun x * y } // 一般export输出变量就是原本的名字,但能够用as关键字重命名 export { multiply as stream1, multiply as stream2 } // multiply能够用两个不一样的名字输出两次
须要特别注意的是,export
命令规定的是对外的接口,必须与模块内部的变量创建一一对应关系。ui
// 报错 export 1 // 报错 var m = 1 export m // 以上两种写法,没有提供对外的接口,都是直接输出1。只是一个值,不是接口 // 写法一 export var m = 1 // 写法二 var m = 1 export {m} // 写法三 var n = 1 export {n as m} // 一样对function和class的输出也必须遵照接口名与模块变量一一对应的关系 // 报错 function f(){} export f // 正确 export function f(){} // 正确 function f(){} export {f}
另外,export
语句输出的接口,与其对应的值是动态绑定关系,即经过该接口,能够取到模块内部实时的值。this
export var foo = 'bar' setTimeout(() => foo='baz',500)
使用export
命令定义了模块的对外接口后,其余JS文件就能够经过import
命令加载这个模块。
import {firstName,lastName,year} from './profile' function setName(element){ element.textContent = firstName + '' + lastName } // 使用as关键字将输入的变量重命名 import {lastName as surname} from './profile' // import后面的from指定模块文件的位置,可使相对路径也能够是绝对路径,.js后缀能够省略 // import命名具备提高效果,会提高到整个模块的头部,首先执行 foo() import {foo} from 'my_module' // 因为import是静态执行,因此不能使用表达式和变量,这些只有在运行时才能获得结果的语法结构 // 报错 import { 'f' + 'oo' } from 'my_module' // 报错 if(x === 1){ import { foo } from 'module1' }else{ import { foo } from 'module2' }
import
语句会执行所加载的模块,所以能够有如下写法
import 'lodash' // 屡次重复执行同一句import语句,那么只会执行一次 import 'lodash' import 'lodash' // import语句是Singleton模式 import {foo} from 'my_module' import {bar} from 'my_module' // 等同于 import {foo,bar} from 'my_module'
目前阶段,经过 Babel 转码,CommonJS 模块的require
命令和 ES6 模块的import
命令,能够写在同一个模块里面,可是最好不要这样作。由于import
在静态解析阶段执行,因此它是一个模块之中最先执行的。
除了指定加载某个输出值,还可使用总体加载,即用星号*
指定一个对象,全部输出值都加载在这个对象上。
import * as circle from './circle' // 注意:模块总体加载所在的对象,应该是能够静态分析的,因此不容许运行时改变 circle.foo = 'hello' circle.area = function(){}
为了使用方便,在不须要用户知道所要加载的变量名或者函数名时就能加载模块,须要使用到export default
命名,为模块指定默认输出。
export default function (){ console.log('foo') } // 加载时,import命令能够为该匿名函数指定任意名字 import customName from './export-default' customName() // 'foo' // export default命令用在非匿名函数前也能够 export default function foo(){} // 另外一种写法 function foo(){} export default foo
export default
命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,所以export default
命令只能使用一次。因此,import
命令后面才不用加大括号,由于只可能对应一个方法。
本质上,export default
就是输出一个叫作default
的变量或方法,而后系统容许你为它取任意名字。
function add(){} export {add as default} // 等同于 export default add import {default as foo} from 'modules' // 等同于 import foo from 'modules'
一样地,由于export default
本质是将该命令后面的值,赋给default
变量之后再默认,因此直接将一个值写在export default
以后。
// 正确 export default 10 // 报错 export 10
能够在一条import
语句中,同时输入默认方法和其余接口
import _,{each,each as forEach} from 'lodash' // 对应的export语句 export default function(){} export function each(){}
export default
也能够用来输出类。
export default class{} import MyClass from 'myClass' let o = new MyClass()
// 在一个模块中,同时输入输出同一个模块 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'
import
命令会被 JavaScript 引擎静态分析,先于模块内的其余模块执行(叫作”链接“更合适)。import
和export
命令只能在模块的顶层,不能在代码块之中。这样的设计致使没法在运行时加载模块。在语法上,条件加载就不可能实现。
若是import
命令要取代 Node 的require
方法,这就造成了一个障碍。由于require
是运行时加载模块,import
命令没法取代require
的动态加载功能。
// 报错 if (x === 2) { import MyModual from './myModual'; } // require加载的模块只有运行时才知道,import语句作不到这一点 const path = './' + fileName const myModual = require(path)
在这种状况下,产生一个提案,建议引入import()
函数,完成动态加载。import()
返回一个Promise对象完成异步加载。这样就相似于Node的require
方法,不一样于require
方法的同步加载。