ES6 模块是编译时加载,编译时就能确定模块的依赖关系,以及输入和输出的变量,相比于CommonJS 和 AMD 模块都只能在运行时确定输入输出变量的加载效率要高。
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
)其中,尤其需要注意this
的限制。ES6 模块之中,顶层的this
指向undefined
,即不应该在顶层代码使用this
一个模块就是一个独立的文件,该文件内部的所有变量,外部都无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量。
如果你引入变量,即使你引入执行了该模块你仍然无法获取模块的变量。
// a.js let str = ‘aaaa‘ //b.js import ‘./a.js‘ //相当于执行了 a.js 文件代码 console.log(str); //报错 str is not defined
export
命令可以出现在模块的任何位置,但必须处于模块顶层,如果处于块级作用域内,就会报错(即不能包含在任何代码块中,比如不能在函数体内)。
export 命令有多种写法,下面将逐一介绍:
// a.js export var firstName = ‘Michael‘; export var lastName = ‘Jackson‘; export var year = 1958; export function fn(x, y) { return x * y; };
上面代码是a.js
文件,保存了用户信息。ES6 将其视为一个模块,里面用export
命令对外部输出了三个变量。
这是推荐使用的输出方式,因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
// a.js var firstName = ‘Michael‘; var lastName = ‘Jackson‘; var year = 1958; export { firstName, lastName, year };
在export
命令后面,使用大括号指定所要输出的一组变量。它与 export 直接加声明语句是等价的,但是应该优先考虑使用这种写法。
通常情况下,export
输出的变量就是本来的名字,但是可以使用as
关键字重命名。as 关键字后面的变量名是输出的名字。
let str = ‘aaa‘ function v1() { ... } function v2() { ... } export { str as str2, v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion };
使用重命名,同一个变量可以用不同的名字输出两次,这样在外部可以用不同的名字引入该变量。
使用前面的语法进行输出时,输入的用户必须得知道输出的变量名才能使用,有时这并不怎么方便。使用export default
命令可以为模块指定默认输出,用户可以为输入的变量起一个任意的名字,就不需要提前知道输出的变量名便可上手使用了。
// a.js 输出 export default function foo() { console.log(‘foo‘); } //或者写成下面两种都可以 export default function() { console.log(‘foo‘); } //先定义,再default输出 function foo() { console.log(‘foo‘); } export default foo; //b.js 输入 import fn from ‘./export-default‘; fn(); // ‘foo‘
上面代码的import
命令,可以用任意名称指向a.js
输出的方法,这时就不需要知道原模块输出的函数名。default 命令后面的变量名在模块外部是无效的,同匿名函数输出的形式一致。
注意,使用 export default 命令输出的模块,import
命令后面不需要大括号。
export
命令不能直接输出变量,因为变量的值必须在运行阶段才能确定,而 export 命令的输出是在编译阶段就已经输出。
export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错 export 1; // 报错 var m = 1; export m; // 报错 function f() {} export f;
如何理解?(可以参考:https://www.imooc.com/wenda/detail/458477)
export 1 ,这里输出的是一个值 1,没有与任何模块内部变量建立联系,所以直接报错。
var m = 1; export m; 这里看起来就像是输出了一个变量m作为对外的接口,我们可能会认为 m 这个变量被输出到模块外被使用,并且与模块内的 m 产生了引用的关系。然而现实情况是,变量m在模块中作为一个变量存在,但是通过export导出m时,被导出的只有m的值 1,所以同样不与内部变量产生联系,于是报错。
这跟函数的传参是一个道理:
let x = 1; //声明变量 const foo=(x)=>{x=2}; //声明一个函数 foo(x) //传入x console.log(x) // 1
上面代码中,变量 x 作为 foo 的参数,只把变量 x 的值传入 foo,x 只作为数值的载体,函数内部 x 并没有与变量 x 产生直接联系。只是复制了变量 x 的值(这种复制值然后再使用的形式与CommonJS加载模式类似)。
使用export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块。
import
命令输入的变量都是只读的,不可以在加载后修改引入的变量。但如果引入的变量是对象的话,可以修改对象的属性,但非常不推荐使用。
import {obj} from ‘./a.js‘ obj = {}; // Syntax Error : ‘obj‘ is read-only;
上面代码中,对引入变量重新赋值就会报错。但是,如果a
是一个对象,改写a
的属性是允许的,但非常不推荐修改引入的变量,因为其他引入的模块也可以读到改写后的值,这种写法很难查错,所以凡是输入的变量,都当作完全只读,轻易不要改变它的属性。
import
命令具有提升效果,会提升到整个模块的头部,首先执行,因为 import 命令是编译阶段执行的,在代码运行之前。
foo(); import { foo } from ‘my_module‘; //这里不会报错,因为import的执行早于foo的调用
import
语句会执行所加载的模块,因此可以有下面的写法。如果多次重复执行同一句import
语句,那么只会执行一次模块的文件代码,而不会执行多次。也就是说import
语句是 Singleton 模式。
import ‘a.js‘; //这里仅仅执行lodash模块,但是不输入任何值,所以并不能使用 a.js 里面定义的变量 //下面代码加载了两次lodash,但是只会执行一次。 import ‘lodash‘; import ‘lodash‘; import { foo } from ‘my_module‘; import { bar } from ‘my_module‘; // 等同于 import { foo, bar } from ‘my_module‘;
目前阶段,通过 Babel 转码,require
命令和import
命令可以写在同一个模块里面,但是最好不要这样做。因为import
在静态解析阶段执行,所以它是一个模块之中最早执行的。
//下面的代码可能不会得到预期结果,因为import将会于require之前执行 require(‘core-js/modules/es6.symbol‘); require(‘core-js/modules/es6.promise‘); import React from ‘React‘;
import 的语法有多种,下面将逐一介绍:
// a.js 输出 var firstName = ‘Michael‘; var lastName = ‘Jackson‘; export { firstName, lastName}; // b.js 输入 import { firstName, lastName} from ‘./a.js‘; //引入后可以直接使用 console.log(firstName,lastName)
如果想为输入的变量重新取一个名字可以使用as
关键字,将输入的变量重命名。as 关键字后面的是输入的变量名,即你想使用的名字
import { firstName as newName } from ‘./a.js‘;
除了指定加载某个输出值,还可以使用整体加载,即加载模块的整个输出对象。用星号(*
)指定一个对象,所有输出值都将加载在这个对象上面。
// a.js export var area = ‘aaa‘ export function circumference(radius) { return 2 * Math.PI * radius; } //b.js import * as newObj from ‘./circle‘; console.log(newObj .area); console.log(newObj .circumference(14));
不允许修改整体加载的对象
import * as circle from ‘./circle‘; // 下面两行都是不允许的 circle.foo = ‘hello‘; circle.area = function () {};
import不
能使用表达式和变量,因为 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‘; }
原文:https://www.cnblogs.com/wenxuehai/p/11312851.html