关于ES6模块加载的一点小疑惑。

xx.js
1.
export default function f() { }
f = 'change'
// { default: change }

2.
function f() { }
export { f as default }
f = 'change'
// { default: change }

3.
function fo() { }
export default fo
fo = 'is not'
// { default: [Function: fo] }

4.
export default (function foo() {})
foo = 'is not'
// 抛出 defined 错误

//是作为运行结果,由另一模块为
import * as o from 'xx.js'
console.log(o)

早上闲来无事看看es6模块加载,自己写了上面几个例子,2,3,4都能够自圆其说,但是并不理解1,为什么会被改变。

阅读 2.7k
2 个回答

你这些写法都是不同的,所以他们导出的绑定对象也不同,由于分别对应ES6规范里的不同章节,我给你分别说一下。

第一个写法是直接export default 函数声明定义,对应的是export default HoistableDeclaration,它的ExportedBindings是BoundNames,按照规范它的BoundNames是这么获取的:

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.

也就是一个包含了原有绑定名的List。
所以你第一个export default,导出名里有两个,其中一个有f,当你在原来模块改变f的时候就会影响到导出的变量。


第二个export {f as default},表面上看是和第一个等效,但它对应的规范是export ExportClause,其中ExportClause在这里是{ ExportsList },ExportsList在这里是ExportSpecifier,ExportSpecifier在这里是IdentifierName as IdentifierName。

它的ExportedBindings规范里是这么写的:

Return a List containing the StringValue of the first IdentifierName.

这里的first IdentifierName就是你的f,所以导出的Binding List里包含了f这个名字,这样就会让你在原有模块下改变f就会影响到导出的变量。


第三个export default fo ,这里的fo事实上不是函数声明定义,它对应的规范是export default AssignmentExpression,和第一个一样,它的ExportedBindings是BoundNames,但是按照规范它的BoundNames是这么获取的:

ExportDeclaration : export default AssignmentExpression ;

  1. Return «"default"».

可以看到这里的BoundNames只返回了一个«"default"»,没有原来的fo什么的,所以你在原模块里不管怎么改变fo,都和导出的变量无关了。

其实和这个类似:
export let default1 = foo
这个指定后,你改变foo=3,不会影响到default1的。
当然这个是举例,实际上这个规范和export default是不同条数的。


第四个export default (function f(){}),export default后面的是函数表达式,并不是函数声明定义,所以它的规范和第三个一样的,导出的ExportedBindings也就是一个«"default"»。
这里之所以报错是因为函数表达式只会返回函数本身作为值,并不会在外部作用域定义同名变量,所以下面的foo = 'is not'会找不到f这个定义。
类似于

var a = function xxx(){}
typeof xxx // undefined

小结一下你的四个写法ExportedBindings

  1. 包含f和default的List
  2. 包含f的List
  3. 包含default的List
  4. 包含default的List

ES6规范可以点这里查看 15.2.3.2 Static Semantics: BoundNames

export default function f() { }
f = 'change'

用babel(ES2015,stage-2)转换后:

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = f;
function f() {}
exports.default = f = 'change';

另外es6不建议用function关键字(有变量提升的效果)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题