JavaScript 模块化 Export 命名子句导出 的一些想法

2020-10-31 23:20:10 +08:00
 haishiwuyuehao

export 常见有几种导出方式:命名行内导出,命名子句导出,默认导出

命名子句导出是通过在大括号内指定导出的变量。

const foo = 'foo';
const bar = 'bar';
export {foo};
export {bar as baz, foo};
export {foo, bar};
在这里我想到,在 JavaScript 中 {} 是一个块。每个块都有其作用域

因此子句导出是创建了一个单独的作用域,在该作用域内指定要导出的变量,但是这么想的话 在指定的作用域中又不能创建方法并返回

例如做到这样:export { hi(){} } 


当我通过命名行导出和默认导出方法时:

export function hi(){};

export default function(){};

对于该 export 语句
1,创建了 hi 方法 并
2,返回 hi 方法的引用 给 export

3,export 创建 default 
4,创建 匿名方法
5,返回匿名方法的引用给 default

从 1-2,3-5 来看,对于方法都是创建了一个作用域块的引用给到 export 。而在 javascript 中 {} 也是简写对象的一种形式,在这里并不是为 export 创建一个对象,返回该对象的引用。

那么我的问题来了,如果对于 export “{}”并不是一个作用域块,不能在其中声明方法,初始变量等。也不是对象简写,那么 子句块的作用就只限于用 as 进行别命名了。真的有这个必要么?在导出命名行进行 as 不是更好吗??
1060 次点击
所在节点    问与答
7 条回复
yafoo
2020-10-31 23:40:51 +08:00
一直都记不清 export 有几种写法,也不知道为啥搞这么多写法
noe132
2020-10-31 23:54:40 +08:00
啥叫作用域块的引用?
只有值才有引用,作用域块怎么说都不像是一种值吧?

不要把 对象字面量语法和语句块语法 和这个大括号搞混在一起,这 3 个都不是 1 个东西,不是所有的大括号都是对象或者语句块,就比如模板字符串 `${a}`,建议把基本的语法学习一下

你只需要知道 每个模块有自己的 namespace,export 只是单纯的在这个 namespace 上添加对应的绑定而已。
export let a = 1
export function b() { a += 1}
export default 'c'
export { a as d }
这 4 个语句都是一样的,在 namespace 上添加绑定。通常 namespace 是一个对象,这个 namespace log 出来大致长这样
{ a: 1, b: function b(){ a += 1 }, _default: 'c', d: 1 }
但不要把 namespace 看作简单的对象,首先它是不可修改的,其次所有的 export 都是对对应变量的绑定,而不是简单的把值导出来。比如如果你在模块内 修改 a = 2,那么 d 的值也会变成 2,因为 d 只是到 a 变量的一个绑定。
import { a, b, d } from 'module'
console.log(a, d) // 1, 1
b()
console.log(a, d) // 2, 2

具体这个绑定是怎么实现的,通常你不需要关心,不同的运行环境或者编译器有不同的处理方式。
noe132
2020-11-01 00:00:17 +08:00
至于为啥不在定义变量的时候 as,设计的时候就是这么设计的,而且正好和 import 的 as 保持一致。如果硬要在变量定义时 as,我觉得这样的语法怎么设计都不会好看
aaronlam
2020-11-01 00:37:11 +08:00
据我所知,export {}; 这种 export 方法是方便你一股脑的把需要 named export 的塞进去 export,而不用每次都

export const foo = 'bar'
export const foo1 = 'bar1'
export function func() {}
haishiwuyuehao
2020-11-01 07:52:28 +08:00
@noe132 我不理解你的 namespace 指的是什么。如果 export * from './myModule.js' ,按照你说的那么此刻 namespace 是怎么样的,在这里 再 export 与 myModule 模块相同的值,就会发生覆盖。此刻的 namespace 又是怎么样的。

但是在 export 子句并非是一个对象,也不是一个作用域块。

我理解,对于一个 module 来说,每个对 module 的引用都是在访问同一个 module 。在该 module 的 export 里,每个 export 具体指向会被引用在被引用的对象中

```javascript
myModule.js

export const foo = 'foo', bar = 'baz';
export default class{
constructor(baz){
this.baz = baz || bar;
console.log(this.baz);
}
}


other.js

import Baz, * as myModule from './myModule.js';
let bax = new Baz();

let bax2 = new myModule.default(myModule.foo);
```

上面的代码就变成
var myModule = (function(moduleConfig){
moduleConfig.foo = 'foo';
moduleConfig.bar = 'bar';
return moduleConfig;
})(myModule || {});

var myModule = (function(moduleConfig){
moduleConfig.default = class { ... }
return moduleConfig;
})(myModule || {});

同样的,other.js 就变成这样
var other= (function(moduleConfig){
const Baz = myModule.default;
const myModule = myModule;
//操作 1
let bax = new Baz();
//操作 2
let bax2 = new myModule.default(myModule.foo);
})(other);

只有这样才能解释的通,为什么修改在一个模块修改 myModule.js 的 foo = 'abc',其它模块访问到的 foo 是 abc 而不是 foo

每个模块都是 IIFE,最终都将返回一个对象的引用给其它所有模块,以此同步所有模块访问到的是同一个模块。

我的问题 还是 {},在 ecmascript 中,不管是 if(condition){} 还是 function(){} 都是表示一个块,都能在块中定义 function,命名变量。但是在 export 里 却不是这样,为什么,export 的子句要设置的如此奇特。export 子句未来是怎么样的
noe132
2020-11-01 11:03:53 +08:00
你的理解是错误的。你看到的 iife 只是 transpiler 对 esmodule 的一种实现。实际上可能根本没有 iife 。
试想
// a.js
export const a = 1

// b.js
import { a } from 'a'
console.log(a)

transpiler 完全可以编译成
const a = 1
console.log(a)

事实上 webpack 开启 concatenation plugin 后就是这样的结果。
为啥总是要认为 export 是一个块?这不就是一个类似对象的语法么,和 import 语句是一样的,只是一种语法,和作用域块没有一点关系。这就不是一个语句块。如果这是个语句块,那你觉得写出来到底 export 了哪些东西?难道 export 了整个语句块?难道 import 语句的括号内我也要可以定义变量么?

export 是完全静态的,export 只能存在模块顶部,不能在任何语句块内。export 只有标记的作用。要求能在代码不运行的情况分析所有的 import export 。如果按你的说法,export {} 里还能定义变量,写语句,那就不符合静态分析这一要求了。

esmodule 只是一个规范,并不是具体的实现,并且规定了 export 不能变,那就不可能有 export 会有语句块这种问题。不要把 iife 的 module 实现直接和 esmodule 划等号

至于 esmodule 是怎么实现的,那是 transpiler 或者 runtime 具体的事情。你不能因为某一种实现就认为所有的实现都有一样的特性,因为可能某些实现有 bug 或者限制
haishiwuyuehao
2020-11-01 11:40:56 +08:00
@noe132 没说过 export 子句是块。我问的是为什么不是块。变成块不好么。不过你说对,我还是在再解下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/720583

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX