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 不是更好吗??
1
yafoo 2020-10-31 23:40:51 +08:00 via Android
一直都记不清 export 有几种写法,也不知道为啥搞这么多写法
|
2
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 具体这个绑定是怎么实现的,通常你不需要关心,不同的运行环境或者编译器有不同的处理方式。 |
3
noe132 2020-11-01 00:00:17 +08:00
至于为啥不在定义变量的时候 as,设计的时候就是这么设计的,而且正好和 import 的 as 保持一致。如果硬要在变量定义时 as,我觉得这样的语法怎么设计都不会好看
|
4
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() {} |
5
haishiwuyuehao OP @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 子句未来是怎么样的 |
6
noe132 2020-11-01 11:03:53 +08:00 via Android
你的理解是错误的。你看到的 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 或者限制 |
7
haishiwuyuehao OP @noe132 没说过 export 子句是块。我问的是为什么不是块。变成块不好么。不过你说对,我还是在再解下
|