We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
转自深入理解 ES6 模块机制
在 ES6 中,我们知道 import、export 取代了 require、module.exports 用来引入和导出模块,但是如果不了解 ES6 模块特性的话,代码可能就会运行出一些匪夷所思的结果,下面我将通过这篇文章为你揭开 ES6 模块机制特点。
说起 ES6 模块特性,那么就先说说 ES6 模块跟 CommonJS 模块的不同之处。
ES6 模块跟 CommonJS 模块的不同,主要有以下两个方面:
首先第一点,在 CommonJS 模块中,如果你 require 了一个模块,那就相当于你执行了该文件的代码并最终获取到模块输出的 module.exports 对象的一份拷贝。
// a.js var b = require('./b'); console.log(b.foo); setTimeout(() => { console.log(b.foo); console.log(require('./b').foo); }, 1000); // b.js let foo = 1; setTimeout(() => { foo = 2; }, 500); module.exports = { foo: foo, }; // 执行:node a.js // 执行结果: // 1 // 1 // 1
从上述代码可以看出,你获取的只是模块输出对象的一个拷贝, b 中的 foo 已经和 a 中的 foo 已经不相干了,所以如果你想要在 CommonJS 中动态获取模块中的值,那么就需要借助于函数延时执行的特性。
// a.js var b = require('./b'); console.log(b.foo()); setTimeout(() => { console.log(b.foo()); console.log(require('./b').foo()); }, 1000); // b.js let foo = 1; setTimeout(() => { foo = 2; }, 500); module.exports = { foo: () => { return foo; }, }; // 执行:node a.js // 执行结果: // 1 // 2 // 2
所以我们可以总结一下:
1.CommonJS 模块中 require 引入模块的位置不同会对输出结果产生影响,并且会生成值的拷贝 2.CommonJS 模块重复引入的模块并不会重复执行,再次获取模块只会获得之前获取到的模块的拷贝
然而在 ES6 模块中就不再是生成输出对象的拷贝,而是动态关联模块中的值。
// a.js import { foo } from './b'; console.log(foo); setTimeout(() => { console.log(foo); import('./b').then(({ foo }) => { console.log(foo); }); }, 1000); // b.js export let foo = 1; setTimeout(() => { foo = 2; }, 500); // 执行:babel-node a.js // 执行结果: // 1 // 2 // 2
关于第二点,ES6 模块编译时执行会导致有以下两个特点:
1.import 命令会被 JavaScript 引擎静态分析,优先于模块内的其他内容执行。 2.export 命令会有变量声明提前的效果。
import 优先执行:
从第一条来看,在文件中的任何位置引入 import 模块都会被提前到文件顶部。
// a.js console.log('a.js') import { foo } from './b'; // b.js export let foo = 1; console.log('b.js 先执行'); // 执行结果: // b.js 先执行 // a.js
从执行结果我们可以很直观地看出,虽然 a 模块中 import 引入晚于 console.log('a'),但是它被 JS 引擎通过静态分析,提到模块执行的最前面,优于模块中的其他部分的执行。
由于 import 是静态执行,所以 import 具有提升效果即 import 命令在模块中的位置并不影响程序的输出。
export 变量声明提升:
正常的引入模块是没办法看出变量声明提升的特性,需要通过循环依赖加载才能看出。
// a.js import { foo } from './b'; console.log('a.js'); export const bar = 1; export const bar2 = () => { console.log('bar2'); } export function bar3() { console.log('bar3'); } // b.js export let foo = 1; import * as a from './a'; console.log(a); // 执行结果: // { bar: undefined, bar2: undefined, bar3: [Function: bar3] } // a.js
从上面的例子可以很直观地看出,a 模块引用了 b 模块,b 模块也引用了 a 模块,export 声明的变量也是优于模块其它内容的执行的,但是具体对变量赋值需要等到执行到相应代码的时候。(当然函数声明和表达式声明不一样,这一点跟 JS 函数性质一样,这里就不过多解释)
好了,讲完了 ES6 模块和 CommonJS 模块的不同点之后,接下来就讲讲相同点:
这个很好理解,无论是 ES6 模块还是 CommonJS 模块,当你重复引入某个相同的模块时,模块只会执行一次。
// a.js import './b'; import './b'; // b.js console.log('只会执行一次'); // 执行结果: // 只会执行一次
结合上面说的特性,我们来看一个比较经典的例子,循环依赖,当你理解了上面所讲的特性之后,下次遇到模块循环依赖代码的执行结果就很容易理解了。
CommonJS之所以能够循环依赖,是因为它被引用的只是一份拷贝,会被缓存起来,它本身的文件,只会在第一次require时执行一次。
跟 CommonJS 模块一样,ES6 不会再去执行重复加载的模块,又由于 ES6 动态输出绑定的特性,能保证 ES6 在任何时候都能获取其它模块当前的最新值。
ES6 模块在编译时就会静态分析,优先于模块内的其他内容执行,所以导致了我们无法写出像下面这样的代码:
if(some condition) { import a from './a'; }else { import b from './b'; } // or import a from (str + 'b');
因为编译时静态分析,导致了我们无法在条件语句或者拼接字符串模块,因为这些都是需要在运行时才能确定的结果在 ES6 模块是不被允许的,所以 动态引入 import() 应运而生。
import() 允许你在运行时动态地引入 ES6 模块,想到这,你可能也想起了 require.ensure 这个语法,但是它们的用途却截然不同的。
我们先来看下它的用法:
动态的 import() 提供一个基于 Promise 的 API 1.动态的import() 可以在脚本的任何地方使用 2.import() 接受字符串文字,你可以根据你的需要构造说明符
// a.js const str = './b'; const flag = true; if(flag) { import('./b').then(({foo}) => { console.log(foo); }) } import(str).then(({foo}) => { console.log(foo); }) // b.js export const foo = 'foo'; // babel-node a.js // 执行结果 // foo // foo
The text was updated successfully, but these errors were encountered:
No branches or pull requests
转自深入理解 ES6 模块机制
前言
在 ES6 中,我们知道 import、export 取代了 require、module.exports 用来引入和导出模块,但是如果不了解 ES6 模块特性的话,代码可能就会运行出一些匪夷所思的结果,下面我将通过这篇文章为你揭开 ES6 模块机制特点。
ES6 模块特性
说起 ES6 模块特性,那么就先说说 ES6 模块跟 CommonJS 模块的不同之处。
ES6 模块跟 CommonJS 模块的不同,主要有以下两个方面:
这个怎么理解呢?我们一步步来看:
CommonJS 输出值的拷贝
首先第一点,在 CommonJS 模块中,如果你 require 了一个模块,那就相当于你执行了该文件的代码并最终获取到模块输出的 module.exports 对象的一份拷贝。
从上述代码可以看出,你获取的只是模块输出对象的一个拷贝, b 中的 foo 已经和 a 中的 foo 已经不相干了,所以如果你想要在 CommonJS 中动态获取模块中的值,那么就需要借助于函数延时执行的特性。
所以我们可以总结一下:
1.CommonJS 模块中 require 引入模块的位置不同会对输出结果产生影响,并且会生成值的拷贝
2.CommonJS 模块重复引入的模块并不会重复执行,再次获取模块只会获得之前获取到的模块的拷贝
ES6 输出值的引用
然而在 ES6 模块中就不再是生成输出对象的拷贝,而是动态关联模块中的值。
ES6 静态编译,CommonJS 运行时加载
关于第二点,ES6 模块编译时执行会导致有以下两个特点:
1.import 命令会被 JavaScript 引擎静态分析,优先于模块内的其他内容执行。
2.export 命令会有变量声明提前的效果。
import 优先执行:
从第一条来看,在文件中的任何位置引入 import 模块都会被提前到文件顶部。
从执行结果我们可以很直观地看出,虽然 a 模块中 import 引入晚于 console.log('a'),但是它被 JS 引擎通过静态分析,提到模块执行的最前面,优于模块中的其他部分的执行。
由于 import 是静态执行,所以 import 具有提升效果即 import 命令在模块中的位置并不影响程序的输出。
export 变量声明提升:
正常的引入模块是没办法看出变量声明提升的特性,需要通过循环依赖加载才能看出。
从上面的例子可以很直观地看出,a 模块引用了 b 模块,b 模块也引用了 a 模块,export 声明的变量也是优于模块其它内容的执行的,但是具体对变量赋值需要等到执行到相应代码的时候。(当然函数声明和表达式声明不一样,这一点跟 JS 函数性质一样,这里就不过多解释)
好了,讲完了 ES6 模块和 CommonJS 模块的不同点之后,接下来就讲讲相同点:
模块不会重复执行
这个很好理解,无论是 ES6 模块还是 CommonJS 模块,当你重复引入某个相同的模块时,模块只会执行一次。
结合上面说的特性,我们来看一个比较经典的例子,循环依赖,当你理解了上面所讲的特性之后,下次遇到模块循环依赖代码的执行结果就很容易理解了。
CommonJS 模块循环依赖
CommonJS之所以能够循环依赖,是因为它被引用的只是一份拷贝,会被缓存起来,它本身的文件,只会在第一次require时执行一次。
ES6 模块循环依赖
跟 CommonJS 模块一样,ES6 不会再去执行重复加载的模块,又由于 ES6 动态输出绑定的特性,能保证 ES6 在任何时候都能获取其它模块当前的最新值。
动态 import()
ES6 模块在编译时就会静态分析,优先于模块内的其他内容执行,所以导致了我们无法写出像下面这样的代码:
因为编译时静态分析,导致了我们无法在条件语句或者拼接字符串模块,因为这些都是需要在运行时才能确定的结果在 ES6 模块是不被允许的,所以 动态引入 import() 应运而生。
import() 允许你在运行时动态地引入 ES6 模块,想到这,你可能也想起了 require.ensure 这个语法,但是它们的用途却截然不同的。
我们先来看下它的用法:
动态的 import() 提供一个基于 Promise 的 API
1.动态的import() 可以在脚本的任何地方使用
2.import() 接受字符串文字,你可以根据你的需要构造说明符
The text was updated successfully, but these errors were encountered: