已默认读者了解本篇自言自语的context,且对于module有所了解,对于module的相关扩展说明将穿插在内容中
(本篇不会提及class)
,由于是只是export,所以......import请容我忽略
本篇代码运行环境为
{"presets": [ "es2015","stage-2" ] }
这只是作为参考,且只是运行环境,不推荐在学习ES6时将代码全部转译为ES5,ES6转化后的代码只能告诉你结果,相比较而言,原因或是理由的价值超过结果,学习ES6,不单单要知道代码运行的结果,最重要的目的是,知其所以然,了解一个行为为什么会这么发生,行为的背后又是什么,这才是学习者所需要追寻的
文章参考:
You-Dont-Know-JS
ECMAScript 6 入门
ES6规范15.2.3.2 Static Semantics: BoundNames
首先登场的就是 export , 这是一个主要关键词,基本用法是放置在一个"声明"之前,或一组由{}
语法(注意,此处的{}语法与对象无关)
包裹的即将被导出的"标识符"之前
//export 放置在"声明"之前
export var a = 1, b = 2, c = 3
export let a = 1
export const a = 1
export let { a } = { a: 1 }
export var foo = function() {}
export function foo() {}
//export 放置在一组"标识符"之前
var a = 1, b = 2
export { a, b }
//等同于
export var a = 1
export var b = 2
以上例子有一个明确的共同点,export 后面没有出现“表达式”。实际上,单独的export 是对变量标识符(指针位置)的绑定,并期许将来会把对应的标识符(指针)导出。
将“变量标识符”导出,这样的描述容易产生混淆,考虑下面的代码
var a = 1
export { a }
a = 3
//等同于
export var a = 1
a = 3
当这个模块被导出后,如果赋值发生,那么已被导出的值也将被更新,无论导出发生在任何阶段。进一步的说,在被导入时的值是无关紧要的。这些绑定是实时的链接,所以唯一重要的是当你访问这个绑定时它当前的值是什么。
- 声明
- 导出标识符 a,此刻的 a 是指向变量本身的一个引用,或指针,而不是它的值的一个拷贝
- 模块内部,a 被重新赋值,已经导出的值也会被自动更新
参考规范15.2.3.2
ExportDeclaration : export VariableStatement
1. Return the BoundNames of VariableStatement.
ExportDeclaration : export Declaration
1. Return the BoundNames of Declaration.
可以看到export 的导出是明确的
另外“标识符”一词引用于The above rule means that each ReferencedBindings of ExportClause is treated as an IdentifierReference.主要是词穷,并且因为模块导出与赋值是不同的,在进行导出时,实质上是导出了一个单向绑定的
(不允许在导入的一方进行改变)
变量的引用,是的,更准确的说应该是对于变量这个容器的引用,模块导出并不关心变量的值。
在进行导出的时候,可以使用别名,关键词 as
var a = 1
export { a as b }
//将 a 重命名为 b
"在一个模块中,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。"
"为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。"
上述两句直接摘自ECMAScript 6 入门,主要是感觉是很恰当的描述,如果加以变动反倒画蛇添足了,当然还不够全面,所以对default进来以下的补充
每个模块定义只能有一个default,它是唯一的,每个被导出的模块只包含一个default元素,所以export default命令在模块内只被允许使用一次。
本质上export default就是输出一个叫做default的默认标识符。等同于,将export default 之后的内容以赋值的形式添加到default元素上。
var a = 1
export default a
//等同于
export default 1
//导出的是那表达式在那一刻的值的绑定,不是 标识符a的绑定(export.default = 表达式)
export default 有许多微妙的细节,令人困扰的(不是结果,而是行为)。请思考下面的代码。
//m2.js
1.
function foo() {}
export default foo
foo = 'change'
2.
export default (function foo() {})
foo = 'change'
3.
function foo() {}
export { foo as default }
foo = 'change'
//由于上文已经描述过,default接近于标识符,所以,可以直接重命名foo作为default导出。
4.
export default function foo() {}
foo = 'change'
//另一模块
import * as all from 'm2.js'
console.log(all)
你的大脑能够清晰的知道每个模块即将会发生的事情吗?如果不能那么请继续阅读,如果能,那么也希望你继续阅读,重温复习这一片段。下面让我复制代码,描述并解释每一模块的行为。
模块1.
function foo() {}
//声明foo
export default foo
//将foo赋值给default元素(注意,此时foo是表达式)
foo = 'change'
//结果
{ default: [Function: foo] }
export default 导出的是那一个函数表达式在那一刻的值的绑定,不是 标识符foo的绑定。换句话说,export default ..接收一个表达式。如果你稍后在你的模块内部赋给foo一个不同的值,这个模块导入将依然表示原本被导出的函数,而不是那个新的值。
规范里定义了export default 表达式的导出相关行为export default AssignmentExpression
ExportDeclaration : export default AssignmentExpression ;
1.Return «"default"».
简单解释下,就是将表达式的值赋予default,然后返回default
模块2.
export default (function foo() {})
将(..)赋值给default 元素(注意,()是表达式)
foo = 'change'
//结果
ReferenceError: foo is not defined
export default !function foo() {}
!等运算符可以包装一个函数使它作为一个表达式返回值
export default (function foo(){}),export default后面的是函数表达式,并不是函数声明定义,所以它对应的规范与模块1相同,导出的ExportedBindings也就是一个«"default"»。
这里之所以报错是因为函数表达式只会返回函数本身作为值,并不会在外部作用域定义同名变量,所以下面的foo = 'change'找不到foo这个定义。
模块3.
function foo() {}
export { foo as default }
foo = 'change'
//结果
{ default: 'change' }
ExportDeclaration : export Declaration
1.Return the BoundNames of Declaration.
行为与 export '标识符‘相同,所以引用的规范相同,唯一需要理解的是default是可以被赋值
模块4.
export default function foo() {}
//一个函数声明出现了!
foo = 'change'
//结果
{ default: 'change' }
function foo..部分是一个函数表达式,但是对于模块内部作用域来说,它被视为一个函数声明,因为名称foo被绑定在模块的顶层作用域
export default 函数声明定义在规范中定义的行为,对应的是export default HoistableDeclaration.
ExportDeclaration : export default HoistableDeclaration
1.Let declarationNames be the BoundNames of HoistableDeclaration.
2.If declarationNames does not include the element "default", append "default" to declarationNames.
3.Return declarationNames.
可以看到按照规范如果将要导出的声明没有包含元素default,那么就进行赋值(规范概念),最后返回的是一个当前绑定的标识符,与前面的表达式时状态不同。所以结果能够看到,导出值被更新了。
原标题为ES6 Module 详解,然后发现自己并没有真正理解Module这部分于是停笔,回去看书了,实际意义上本文是为了总结知识
初学ES6,文章有误请指点,文内部分用词不准确也请谅解,虽然引用了规范,但是并没有能力进行解读,惭愧。
文章参考:
You-Dont-Know-JS
ECMAScript 6 入门
ES6规范15.2.3.2 Static Semantics: BoundNames