摘要:背景现在最主流的模块机制是和。模块模块导出的变量始终指向的是模块内部的变量,使用时可以获得此变量的最新值。我们叫导出绑定。但是在看对默认导出代码的转换时,发现实现并不使用。所以改了并不等于改了,自然的东西没有变化。
背景
JavaScript 现在最主流的模块机制是 commonjs 和 ES6 module。两者不单是语法上有所区别,在加载的时候也有所不同,譬如 commonjs 是运行时加载,ES6模块是编译时输出接口的。还有一个重要的区别是 commonjs 导出的东西是值拷贝,而 ES6 模块导出的东西是……暂时认为是引用拷贝吧。具体表现出来的区别看下面的例子:
commonjs模块// a.js var counter = 3; function incCounter() { counter++; } module.exports = { counter: counter, incCounter: incCounter, }; // main.js var mod = require("./a"); console.log(mod.counter); // 3 mod.incCounter(); console.log(mod.counter); // 3
可以看到 counter 在模块内部被改变,但是使用此模块的代码获取 counter 始终是 export 时候的值,不会变。
ES6模块// a.js export let counter = 3; export function incCounter() { counter++; } // main.js import { counter, incCounter } from "./a"; console.log(counter); // 3 incCounter(); console.log(counter); // 4
ES6 模块导出的变量始终指向的是模块内部的变量,使用时可以获得此变量的最新值。我们叫导出绑定:Exporting binding。
问题如果你去看 webpack 编译后的实现,它会把 counter 变量转换成 counter 的 getter,这么就可以实现绑定的效果。但是在看 webpack 对默认导出代码的转换时,发现实现并不使用 getter。也就是说按这种实现,使用 export default counter 是不会产生 Exporting binding。看看代码:
// a.js let counter = 3; export function incCounter() { counter++; } export default counter; // main.js import counter, { incCounter } from "./a"; console.log(counter); // 3 incCounter(); console.log(counter); // 3解释
为什么会有这种效果?其实 export default 是一种语法糖,当模块只有一个导出的时候,简化写代码人的代码量,我们把这个语法糖还原下:
// 语法糖 // myFunc.js function myFunc() {} export default myFunc; // main.js import myFunc from "./myFunc"; // 非语法糖 // myFunc.js function myFunc() {} export { myFunc as default }; // main.js import { default as myFunc } from "./myFunc";
也就是说把 export 的东西重命名/赋值给 default,再在 import 的时候把 default 重命名为你想要的名字。问题就出在这层语法糖的转换上,规范对于 export default x 的行为有解释,x 的类型不同,则行为不同:
有名字的函数和类export default function foo() {} export default class Bar {}
相当于
function foo() {} export { foo as default }; class Bar {} export { Bar as default };没有名字的函数和类
export default function () {} export default class {}
相当于
function *default*() {} export { *default* as default }; class *default* {} export { *default* as default };
JS会把给匿名函数或类给一个内部的变量*default*,然后再重命名为 default 导出。这个内部变量是无法通过程序获取到的。
原始类型export default 1; // --- 或者 --- let x = 4; export default x;
相当于
let *default* = 1 export { *default* as default }; // --- 或者 --- let x = 4; let *default* = x; export { *default* as default };
当 export default x 中的 x 是没有名字的函数或者类,又或者是原始类型,export binding 的是内部变量*default* 并不是 x。所以改了 x 并不等于改了*default*,自然 import 的东西没有变化。
参考文献https://2ality.com/2015/07/es...
https://stackoverflow.com/que...
https://github.com/rollup/rol...
https://ponyfoo.com/articles/...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/106869.html
摘要:当我们学习的模块化,就会发现它的发展深受的影响。严格模式在模块系统中,严格模式是默认开启的。同样的,模块内部的声明只在模块内部有效。在中,我们使用导入内容在模块中,我们只需要为导入的绑定起一个名字我们也可以导入具名导出的内容。 ES6 模块系统 在 ES6 之前,我们已经知道了 RequireJS,AngularJS 的依赖注入,以及 CommonJS,具体可以看笔者的上一篇文章《JS...
摘要:和必须始终显示在其各自用法的顶级范围中。也就是说不可以在条件中放置或它们必须放在所有块和函数之外。不幸的是,它存在一些缺点,并正式地不鼓励这样做。用户可以另外手动将和列为命名导入,如果他们想要的话。 ES6提供了两个关键字import导入和export导出,语法上有些差别。!important: import和export必须,始终显示在,其各自用法的,顶级范围中。也就是说不可以在if...
摘要:模块可以导入和导出各种类型的变量,如函数,对象,字符串,数字,布尔值,等等。所以这可能会导致一些不符合预期的行为。可变的基本类型值在导入一些基本类型的值如数字,布尔值或字符串时,可能会产生一个有趣的副作用。 前言 ECMAScript 2015(又称ES6)提供了一个前端JavaScript缺失已久的特性 —— 模块。ES2015中的模块参考了CommonJS规范(目前Node.js的...
摘要:盲目使用替换后可能会导致预期意外的结果。在中,许多种方法来处理函数的参数默认值,参数数量,参数命名。此外,处理后的值,无论是解决还是拒绝的结果值,都是不可改变的。 这是一个 ES2015(ES6) 的Cheatsheet,其中包括提示、小技巧、最佳实践和一些代码片段,帮助你完成日复一日的开发工作。 Table of Contents var 与 let / const 声明 代码执行...
摘要:目前主流的模块规范模块通用模块如果你在文件头部看到这样的代码,那么这个文件使用的就是规范实际上就是全局变量这三种风格的结合这段代码就是对当前运行环境的判断,如果是环境就是使用规范,如果不是就判断是否为环境,最后导出全局变量有了后我们的代码和 目前主流的模块规范 UMD CommonJs es6 module umd 模块(通用模块) (function (global, facto...
阅读 1075·2021-09-29 09:35
阅读 4621·2021-09-22 15:24
阅读 1448·2021-07-25 21:37
阅读 2177·2019-08-30 14:17
阅读 964·2019-08-30 13:56
阅读 2410·2019-08-29 17:07
阅读 1247·2019-08-29 12:44
阅读 2704·2019-08-26 18:26