精华内容
下载资源
问答
  • I'm having this problem in ES6 in a Babel Environment:// A.jsclass A {}export default new A();// B.jsimport C from './C';class B {}export default new B();// C.jsimport A from './A';import B from './B'...

    I'm having this problem in ES6 in a Babel Environment:

    // A.js

    class A {

    }

    export default new A();

    // B.js

    import C from './C';

    class B {

    }

    export default new B();

    // C.js

    import A from './A';

    import B from './B';

    class C {

    constructor(A, B){

    this.A = A;

    this.B = B; // undefined

    }

    }

    export default new C(A, B)

    I import them like this:

    // stores/index.js

    import A from './A';

    import B from './B';

    import C from './C';

    export {

    A,

    B,

    C

    }

    And from my app entry point I do:

    import * as stores from './stores';

    I would (hoped) expected the order of execution to be A -> B ->C but in reality it is A-> C-> B.

    This is due to module B importing C, so that the C module is evaluated before the B module. This creates a problem in the setup of C because in that case B would be undefined.

    I've seen a similar question but I'm not sure the init function is the best solution here, it seems a bit hacky.

    Q: What's the best practise about solving this kind of circular dependencies in ES6 that would possibly work in different environments (Babel, Rollup)?

    解决方案What's the best practice about circular dependencies in ES6?

    Avoid them altogether. If you cannot (or don't want to) completely avoid them, restrict the involved modules to only use function declarations with circular dependencies, never initialise top-level values (constants, variables, classes) by using imported values (that includes extends references).

    What would possibly work in different environments (Babel, Rollup)?

    The order of module resolution and initialisation is defined in the ES6 specification, so this should be the same in all ES6 environments - regardless how the modules are loaded and how their identifiers are resolved.

    How to solve this kind of circular dependencies setup?

    If you do have a circular dependency X -> Y -> Z -> … -> X -> …, you need to establish a start point. Say you want X to be loaded first, although it depends on Y, so you need to make sure that X never uses any of the imported values until all modules in the circle are completely initialised. So you break the circle between X and Y, and you will need to start the import chain at Y - it will recursively traverse the dependencies until it stops at X, which has no further dependencies that are not already getting initialised, so it will be the first module to be evaluated.

    The rule you have to follow then is to always import Y before importing any of the other modules in the circle. If you even once do not use this common single entry point to the circle, your house of cards will collapse.

    In your particular example, this means that index.js will need to use

    import A from './A';

    import C from './C'; // entry point to circular dependencies

    import B from './B';

    展开全文
  • 在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是:简单for循环for-inforEach在2015年6月份发布的ECMAScript6(简称 ES6)中,新增了一种循环,是:for-of下面我们就来看看这 4 种 for 循环。简单 for 循环下面先...

    在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是:

    简单for循环

    for-in

    forEach

    在2015年6月份发布的ECMAScript6(简称 ES6)中,新增了一种循环,是:

    for-of

    下面我们就来看看这 4 种 for 循环。

    简单 for 循环

    下面先来看看大家最常见的一种写法:

    当数组长度在循环过程中不会改变时,我们应将数组长度用变量存储起来,这样会获得更好的效率,下面是改进的写法:

    for-in

    通常情况下,我们可以用 for-in 来遍历一遍数组的内容,代码如下:

    一般情况下,运行结果如下:

    但这么做往往会出现问题。

    for-in 的真相

    for-in 循环遍历的是对象的属性,而不是数组的索引。因此, for-in 遍历的对象便不局限于数组,还可以遍历对象。例子如下:

    结果如下:

    需要注意的是, for-in 遍历属性的顺序并不确定,即输出的结果顺序与属性在对象中的顺序无关,也与属性的字母顺序无关,与其他任何顺序也无关。

    Array 的真相

    Array 在 Javascript 中是一个对象, Array 的索引是属性名。事实上, Javascript 中的 “array” 有些误导性, Javascript 中的 Array 并不像大部分其他语言的数组。首先, Javascript 中的 Array 在内存上并不连续,其次, Array 的索引并不是指偏移量。实际上, Array 的索引也不是 Number 类型,而是 String 类型的。我们可以正确使用如 arr[0] 的写法的原因是语言可以自动将 Number 类型的 0 转换成 String 类型的 “0″ 。所以,在 Javascript 中从来就没有 Array 的索引,而只有类似 “0″ 、 “1″ 等等的属性。有趣的是,每个 Array 对象都有一个 length 的属性,导致其表现地更像其他语言的数组。但为什么在遍历 Array 对象的时候没有输出 length 这一条属性呢?那是因为 for-in 只能遍历“可枚举的属性”, length 属于不可枚举属性,实际上, Array 对象还有许多其他不可枚举的属性。

    现在,我们再回过头来看看用 for-in 来循环数组的例子,我们修改一下前面遍历数组的例子:

    运行结果是:

    我们看到 for-in 循环访问了我们新增的 “name” 属性,因为 for-in 遍历了对象的所有属性,而不仅仅是“索引”。同时需要注意的是,此处输出的索引值,即 “0″、 “1″、 “2″不是 Number 类型的,而是 String 类型的,因为其就是作为属性输出,而不是索引。那是不是说不在我们的 Array 对象中添加新的属性,我们就可以只输出数组中的内容了呢?答案是否定的。因为 for-in 不仅仅遍历 array 自身的属性,其还遍历 array 原型链上的所有可枚举的属性。下面我们看个例子:

    运行结果是:

    写到这里,我们可以发现 for-in 并不适合用来遍历 Array 中的元素,其更适合遍历对象中的属性,这也是其被创造出来的初衷。却有一种情况例外,就是稀疏数组。考虑下面的例子:

    for-in 只会遍历存在的实体,上面的例子中, for-in 遍历了3次(遍历属性分别为”0″、 “100″、 “10000″的元素,普通 for 循环则会遍历 10001 次)。所以,只要处理得当, for-in 在遍历 Array 中元素也能发挥巨大作用。

    为了避免重复劳动,我们可以包装一下上面的代码:

    使用示例如下:

    for-in 性能

    正如上面所说,每次迭代操作会同时搜索实例或者原型属性, for-in 循环的每次迭代都会产生更多开销,因此要比其他循环类型慢,一般速度为其他类型循环的 1/7。因此,除非明确需要迭代一个属性数量未知的对象,否则应避免使用 for-in 循环。如果需要遍历一个数量有限的已知属性列表,使用其他循环会更快,比如下面的例子:

    上面代码中,将对象的属性都存入一个数组中,相对于 for-in 查找每一个属性,该代码只关注给定的属性,节省了循环的开销和时间。

    forEach

    在 ES5 中,引入了新的循环,即 forEach 循环。

    运行结果:

    forEach 方法为数组中含有有效值的每一项执行一次 callback 函数,那些已删除(使用 delete 方法等情况)或者从未赋值的项将被跳过(不包括那些值为 undefined 或 null 的项)。 callback 函数会被依次传入三个参数:

    数组当前项的值;

    数组当前项的索引;

    数组对象本身;

    需要注意的是,forEach 遍历的范围在第一次调用 callback 前就会确定。调用forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。

    运行结果:

    这里的 index 是 Number 类型,并且也不会像 for-in 一样遍历原型链上的属性。

    所以,使用 forEach 时,我们不需要专门地声明 index 和遍历的元素,因为这些都作为回调函数的参数。

    另外,forEach 将会遍历数组中的所有元素,但是 ES5 定义了一些其他有用的方法,下面是一部分:

    every: 循环在第一次 return false 后返回

    some: 循环在第一次 return true 后返回

    filter: 返回一个新的数组,该数组内的元素满足回调函数

    map: 将原数组中的元素处理后再返回

    reduce: 对数组中的元素依次处理,将上次处理结果作为下次处理的输入,最后得到最终结果。

    forEach 性能

    首先感谢@papa pa的提醒,才发现我之前的理解有错误。

    大家可以看 jsPerf ,在不同浏览器下测试的结果都是 forEach 的速度不如 for。如果大家把测试代码放在控制台的话,可能会得到不一样的结果,主要原因是控制台的执行环境与真实的代码执行环境有所区别。

    for-of

    先来看个例子:

    运行结果是:

    为什么要引进 for-of?

    要回答这个问题,我们先来看看ES6之前的 3 种 for 循环有什么缺陷:

    forEach 不能 break 和 return;

    for-in 缺点更加明显,它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。

    所以,鉴于以上种种缺陷,我们需要改进原先的 for 循环。但 ES6 不会破坏你已经写好的 JS 代码。目前,成千上万的 Web 网站依赖 for-in 循环,其中一些网站甚至将其用于数组遍历。如果想通过修正 for-in 循环增加数组遍历支持会让这一切变得更加混乱,因此,标准委员会在 ES6 中增加了一种新的循环语法来解决目前的问题,即 for-of 。

    那 for-of 到底可以干什么呢?

    跟 forEach 相比,可以正确响应 break, continue, return。

    for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。

    for-of 循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。

    for-of 也支持 Map 和 Set (两者均为 ES6 中新增的类型)对象遍历。

    总结一下,for-of 循环有以下几个特征:

    这是最简洁、最直接的遍历数组元素的语法。

    这个方法避开了 for-in 循环的所有缺陷。

    与 forEach 不同的是,它可以正确响应 break、continue 和 return 语句。

    其不仅可以遍历数组,还可以遍历类数组对象和其他可迭代对象。

    但需要注意的是,for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用

    for-in 循环(这也是它的本职工作)。

    最后要说的是,ES6 引进的另一个方式也能实现遍历数组的值,那就是 Iterator。上个例子:

    前面的不多说,重点描述for-of

    for-of循环不仅支持数组,还支持大多数类数组对象,例如DOM NodeList对象。

    for-of循环也支持字符串遍历,它将字符串视为一系列的Unicode字符来进行遍历:

    window.οnlοad=function(){

    const arr = [55,00, 11, 22];

    arr.name = "hello";

    // Array.prototype.FatherName = 'FatherName';

    /*for(let key in arr){

    console.log('key='+key+',key.value='+arr[key]);

    }*/

    /* arr.forEach((data) => {console.log(data);});*/

    /* arr.forEach((data,index,arr) => {console.log(data+','+index+','+arr);});*/

    /*for(let key of arr){

    console.log(key);

    }*/

    var string1 = 'abcdefghijklmn';

    var string2 = 'opqrstuvwxyc';

    const stringArr = [string1,string2];

    for(let key of stringArr){

    console.log(key);

    }

    for(let key of string1){

    console.log(key);

    }

    }

    结果:

    现在,只需记住:

    这是最简洁、最直接的遍历数组元素的语法

    这个方法避开了for-in循环的所有缺陷

    与forEach()不同的是,它可以正确响应break、continue和return语句

    for-in循环用来遍历对象属性。

    for-of循环用来遍历数据—例如数组中的值。

    它同样支持Map和Set对象遍历。

    Map和Set对象是ES6中新增的类型。ES6中的Map和Set和java中并无太大出入。

    Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。

    要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:

    var s1 = new Set(); // 空Set

    var s2 = new Set([1, 2, 3]); // 含1, 2, 3

    1

    2

    1

    2

    重复元素在Set中自动被过滤:

    var s = new Set([1, 2, 3, 3, '3']);

    s; // Set {1, 2, 3, "3"}

    1

    2

    1

    2

    通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:

    var s = new Set([1, 2, 3]);

    s.add(4);

    s; // Set {1, 2, 3, 4}

    s.add(4);

    s; // Set {1, 2, 3, 4}

    1

    2

    3

    4

    5

    1

    2

    3

    4

    5

    通过delete(key)方法可以删除元素:

    var s = new Set([1, 2, 3]);

    s; // Set {1, 2, 3}

    s.delete(3);

    s; // Set {1, 2}

    Set对象可以自动排除重复项

    var string1 = 'abcdefghijklmn';

    var string2 = 'opqrstuvwxyc';

    var string3 = 'opqrstuvwxyc';

    var string4 = 'opqrstuvwxyz';

    const stringArr = [string1,string2,string3,string4];

    var newSet = new Set(stringArr);

    for(let key of newSet){

    console.log(key);

    }

    结果:

    Map对象稍有不同:内含的数据由键值对组成,所以你需要使用解构(destructuring)来将键值对拆解为两个独立的变量:

    for (var [key, value] of phoneBookMap) {

    console.log(key + "'s phone number is: " + value);

    }

    var m = new Map([[1, 'Michael'], [2, 'Bob'], [3, 'Tracy']]);

    var map = new Map([['1','Jckey'],['2','Mike'],['3','zhengxin']]);

    map.set('4','Adam');//添加key-value

    map.set('5','Tom');

    map.set('6','Jerry');

    console.log(map.get('6'));

    map.delete('6');

    console.log(map.get('6'));

    for(var [key,value] of map) {

    console.log('key='+key+' , value='+value);

    }

    结果:

    解构也是ES6的新特性,我们将在另一篇文章中讲解。看来我应该记录这些优秀的主题,未来有太多的新内容需要一一剖析。

    现在,你只需记住:未来的JS可以使用一些新型的集合类,甚至会有更多的类型陆续诞生,而for-of就是为遍历所有这些集合特别设计的循环语句。

    for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法:

    // 向控制台输出对象的可枚举属性for(varkey ofObject.keys(someObject)){

    console.log(key +": "+ someObject[key]);

    var obj = { name :'name', age : 'age', sex : 'sex' }; for (var key of Object.keys(obj)) { console.log(key + ": " + obj[key]); }

    结果:

    深入理解

    “能工摹形,巧匠窃意。”——巴勃罗·毕卡索

    ES6始终坚持这样的宗旨:凡是新加入的特性,势必已在其它语言中得到强有力的实用性证明。

    举个例子,新加入的for-of循环像极了C++、Java、C#以及Python中的循环语句。与它们一样,这里的for-of循环支持语言和标准库中提供的几种不同的数据结构。它同样也是这门语言中的一个扩展点(译注:关于扩展点,建议参考 1. 浅析扩展点 2. What are extensions and extension points?)。

    正如其它语言中的for/foreach语句一样,for-of循环语句通过方法调用来遍历各种集合。数组、Maps对象、Sets对象以及其它在我们讨论的对象有一个共同点,它们都有一个迭代器方法。

    你可以给任意类型的对象添加迭代器方法。

    当你为对象添加myObject.toString()方法后,就可以将对象转化为字符串,同样地,当你向任意对象添加myObject[Symbol.iterator]()方法,就可以遍历这个对象了。

    举个例子,假设你正在使用jQuery,尽管你非常钟情于里面的.each()方法,但你还是想让jQuery对象也支持for-of循环,你可以这样做:

    // 因为jQuery对象与数组相似

    // 可以为其添加与数组一致的迭代器方法

    jQuery.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

    好的,我知道你在想什么,那个[Symbol.iterator]语法看起来很奇怪,这段代码到底做了什么呢?这里通过Symbol处理了一下方法的名称。标准委员会可以把这个方法命名为.iterator()方法,但是如果你的代码中的对象可能也有一些.iterator()方法,这一定会让你感到非常困惑。于是在ES6标准中使用symbol来作为方法名,而不是使用字符串。

    你大概也猜到了,Symbols是ES6中的新类型,我们会在后续的文章中讲解。现在,你需要记住,基于新标准,你可以定义一个全新的symbol,就像Symbol.iterator,如此一来可以保证不与任何已有代码产生冲突。这样做的代价是,这段代码的语法看起来会略显生硬,但是这微乎其微代价却可以为你带来如此多的新特性和新功能,并且你所做的这一切可以完美地向后兼容。

    所有拥有[Symbol.iterator]()的对象被称为可迭代的。在接下来的文章中你会发现,可迭代对象的概念几乎贯穿于整门语言之中,不仅是for-of循环,还有Map和Set构造函数、解构赋值,以及新的展开操作符。

    迭代器对象

    现在,你将无须亲自从零开始实现一个对象迭代器,我们会在下一篇文章详细讲解。为了帮助你理解本文,我们简单了解一下迭代器(如果你跳过这一章,你将错过非常精彩的技术细节)。

    for-of循环首先调用集合的[Symbol.iterator]()方法,紧接着返回一个新的迭代器对象。迭代器对象可以是任意具有.next()方法的对象;for-of循环将重复调用这个方法,每次循环调用一次。举个例子,这段代码是我能想出来的最简单的迭代器:

    var zeroesForeverIterator = {

    [Symbol.iterator]: function () {

    return this;

    },

    next: function () {

    return {done: false, value: 0};

    }

    };

    每一次调用.next()方法,它都返回相同的结果,返回给for-of循环的结果有两种可能:(a) 我们尚未完成迭代;(b) 下一个值为0。这意味着(value of zeroesForeverIterator) {}将会是一个无限循环。当然,一般来说迭代器不会如此简单。

    这个迭代器的设计,以及它的.done和.value属性,从表面上看与其它语言中的迭代器不太一样。在Java中,迭代器有分离的.hasNext()和.next()方法。在Python中,他们只有一个.next() 方法,当没有更多值时抛出StopIteration异常。但是所有这三种设计从根本上讲都返回了相同的信息。

    迭代器对象也可以实现可选的.return()和.throw(exc)方法。如果for-of循环过早退出会调用.return()方法,异常、break语句或return语句均可触发过早退出。如果迭代器需要执行一些清洁或释放资源的操作,可以在.return()方法中实现。大多数迭代器方法无须实现这一方法。.throw(exc)方法的使用场景就更特殊了:for-of循环永远不会调用它。但是我们还是会在下一篇文章更详细地讲解它的作用。

    现在我们已了解所有细节,可以写一个简单的for-of循环然后按照下面的方法调用重写被迭代的对象。

    首先是for-of循环:

    for (VAR of ITERABLE) {

    一些语句

    }

    然后是一个使用以下方法和少许临时变量实现的与之前大致相当的示例,:

    var $iterator = ITERABLE[Symbol.iterator]();

    var $result = $iterator.next();

    while (!$result.done) {

    VAR = $result.value;

    一些语句

    $result = $iterator.next();

    }

    这段代码没有展示.return()方法是如何处理的,我们可以添加这部分代码,但我认为这对于我们正在讲解的内容来说过于复杂了。for-of循环用起来很简单,但是其背后有着非常复杂的机制。

    我何时可以开始使用这一新特性?

    目前,对于for-of循环新特性,所有最新版本Firefox都(部分)支持(译注:从FF 13开始陆续支持相关功能,FF 36 - FF 40基本支持大部分特性),在Chrome中可以通过访问 chrome://flags 并启用“实验性JavaScript”来支持。微软的Spartan浏览器支持,但是IE不支持。如果你想在web环境中使用这种新语法,同时需要支持IE和Safari,你可以使用Babel或Google的Traceur这些编译器来将你的ES6代码翻译为Web友好的ES5代码。

    而在服务端,你不需要类似的编译器,io.js中默认支持ES6新语法(部分),在Node中需要添加--harmony选项来启用相关特性。

    {done: true}

    喲!

    好的,我们今天的讲解就到这里,但是对于for-of循环的使用远没有结束。

    在ES6中有一种新的对象与for-of循环配合使用非常契合,我没有提及它因为它是我们下周文章的主题,我认为这种新特性是ES6种最梦幻的地方,如果你尚未在类似Python和C#的语言中遇到它,你一开始很可能会发现它令人难以置信,但是这是编写迭代器最简单的方式,在重构中非常有用,并且它很可能改变我们书写异步代码的方式,无论是在浏览器环境还是服务器环境,所以,下周的深入浅出 ES6 中,请务必一起来仔细看看 ES6 的生成器:generators。

    展开全文
  • CommonJS模块规范使用require语句...ES6模块的规范是使用import语句导入模块,export语句导出模块,输出的是对值的引用。ES6模块的运行机制和CommonJS不一样,遇到模块加载命令import时不去执行这个模块,只会生成一...

    CommonJS模块规范使用require语句导入模块,module.exports导出模块,输出的是值的拷贝,模块导入的也是输出值的拷贝,也就是说,一旦输出这个值,这个值在模块内部的变化是监听不到的。

    ES6模块的规范是使用import语句导入模块,export语句导出模块,输出的是对值的引用。ES6模块的运行机制和CommonJS不一样,遇到模块加载命令import时不去执行这个模块,只会生成一个动态的只读引用,等真的需要用到这个值时,再到模块中取值,也就是说原始值变了,那输入值也会发生变化。

    那CommonJS和ES6模块规范针对模块的循环加载处理机制有什么不同呢?

    循环加载指的是a脚本的执行依赖b脚本,b脚本的执行依赖a脚本。

    1. CommonJS模块的加载原理

    CommonJS模块就是一个脚本文件,require命令第一次加载该脚本时就会执行整个脚本,然后在内存中生成该模块的一个说明对象。

    {

    id: '', //模块名,唯一

    exports: { //模块输出的各个接口

    ...

    },

    loaded: true, //模块的脚本是否执行完毕

    ...

    }

    复制代码

    以后用到这个模块时,就会到对象的exports属性中取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存中取值。

    CommonJS模块是加载时执行,即脚本代码在require时就全部执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,没有执行的部分不会输出。

    案例说明:

    //a.js

    exports.done = false;

    var b = require('./b.js');

    console.log('在a.js中,b.done = %j', b.done);

    exports.done = true;

    console.log('a.js执行完毕!')

    复制代码//b.js

    exports.done = false;

    var a = require('./a.js');

    console.log('在b.js中,a.done = %j', a.done);

    exports.done = true;

    console.log('b.js执行完毕!')

    复制代码//main.js

    var a = require('./a.js');

    var b = require('./b.js');

    console.log('在main.js中,a.done = %j, b.done = %j', a.done, b.done);

    复制代码

    输出结果如下:

    //node环境下运行main.js

    node main.js

    在b.js中,a.done = false

    b.js执行完毕!

    在a.js中,b.done = true

    a.js执行完毕!

    在main.js中,a.done = true, b.done = true

    复制代码

    JS代码执行顺序如下:

    1)main.js中先加载a.js,a脚本先输出done变量,值为false,然后加载b脚本,a的代码停止执行,等待b脚本执行完成后,才会继续往下执行。

    2)b.js执行到第二行会去加载a.js,这时发生循环加载,系统会去a.js模块对应对象的exports属性取值,因为a.js没执行完,从exports属性只能取回已经执行的部分,未执行的部分不返回,所以取回的值并不是最后的值。

    3)a.js已执行的代码只有一行,exports.done = false;所以对于b.js来说,require a.js只输出了一个变量done,值为false。往下执行console.log('在b.js中,a.done = %j', a.done);控制台打印出:

    在b.js中,a.done = false

    复制代码

    4)b.js继续往下执行,done变量设置为true,console.log('b.js执行完毕!'),等到全部执行完毕,将执行权交还给a.js。此时控制台输出:

    b.js执行完毕!

    复制代码

    5)执行权交给a.js后,a.js接着往下执行,执行console.log('在a.js中,b.done = %j', b.done);控制台打印出:

    在a.js中,b.done = true

    复制代码

    6)a.js继续执行,变量done设置为true,直到a.js执行完毕。

    a.js执行完毕!

    复制代码

    7)main.js中第二行不会再次执行b.js,直接输出缓存结果。最后控制台输出:

    在main.js中,a.done = true, b.done = true

    复制代码

    总结:

    1)在b.js中,a.js没有执行完毕,只执行了第一行,所以循环加载中,只输出已执行的部分。

    2)main.js第二行不会再次执行,而是输出缓存b.js的执行结果。exports.done = true;

    2. ES6模块的循环加载

    ES6模块与CommonJS有本质区别,ES6模块对导出变量,方法,对象是动态引用,遇到模块加载命令import时不会去执行模块,只是生成一个指向被加载模块的引用,需要开发者保证真正取值时能够取到值,只要引用是存在的,代码就能执行。

    案例说明:

    //even.js

    import {odd} from './odd';

    var counter = 0;

    export function even(n){

    counter ++;

    console.log(counter);

    return n == 0 || odd(n-1);

    }

    复制代码//odd.js

    import {even} from './even.js';

    export function odd(n){

    return n != 0 && even(n-1);

    }

    复制代码//index.js

    import * as m from './even.js';

    var x = m.even(5);

    console.log(x);

    var y = m.even(4);

    console.log(y);

    复制代码

    执行index.js,输出结果如下:

    babel-node index.js

    1

    2

    3

    false

    4

    5

    6

    true

    复制代码

    可以看出counter的值是累加的,ES6是动态引用。如果上面的引用改为CommonJS代码,会报错,因为在odd.js里,even.js代码并没有执行。改成CommonJS规范加载的代码为:

    //even.js

    var odd = require('./odd.js');

    var counter = 0;

    module.exports = function even(n){

    counter ++;

    console.log(counter);

    return n == 0 || odd(n-1);

    }

    复制代码//odd.js

    var even = require('./even.js');

    module.exports = function odd(n){

    return n != 0 && even(n-1);

    }

    复制代码//index.js

    var even = require('./even.js');

    var x = even(5);

    console.log(x);

    var y = even(5);

    console.log(y);

    复制代码

    执行index.js,输出结果如下:

    $ babel-node index.js

    1

    /Users/name/Projects/node/ES6/odd.1.js:6

    return n != 0 && even(n - 1);

    ^

    TypeError: even is not a function

    at odd (/Users/name/Projects/node/ES6/odd.1.js:4:22)

    复制代码

    3. 总结

    1)CommonJS模块是加载时执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,没有执行的部分不会输出。

    2)ES6模块对导出模块,变量,对象是动态引用,遇到模块加载命令import时不会去执行模块,只是生成一个指向被加载模块的引用。

    CommonJS模块规范主要适用于后端Node.js,后端Node.js是同步模块加载,所以在模块循环引入时模块已经执行完毕。推荐前端工程中使用ES6的模块规范,通过安装Babel转码插件支持ES6模块引入的语法。

    页面内容主要来源于《ES6标准入门》Module 这一章的介绍。如果有描述不清楚或错误的地方,欢迎留言指证。

    参考资料:

    展开全文
  • CommonJS模块规范使用require语句...ES6模块的规范是使用import语句导入模块,export语句导出模块,输出的是对值的引用。ES6模块的运行机制和CommonJS不一样,遇到模块加载命令import时不去执行这个模块,只会生成一...

    CommonJS模块规范使用require语句导入模块,module.exports导出模块,输出的是值的拷贝,模块导入的也是输出值的拷贝,也就是说,一旦输出这个值,这个值在模块内部的变化是监听不到的。

    ES6模块的规范是使用import语句导入模块,export语句导出模块,输出的是对值的引用。ES6模块的运行机制和CommonJS不一样,遇到模块加载命令import时不去执行这个模块,只会生成一个动态的只读引用,等真的需要用到这个值时,再到模块中取值,也就是说原始值变了,那输入值也会发生变化。

    那CommonJS和ES6模块规范针对模块的循环加载处理机制有什么不同呢?

    循环加载指的是a脚本的执行依赖b脚本,b脚本的执行依赖a脚本。

    1. CommonJS模块的加载原理

    CommonJS模块就是一个脚本文件,require命令第一次加载该脚本时就会执行整个脚本,然后在内存中生成该模块的一个说明对象。

    {

    id: '', //模块名,唯一

    exports: { //模块输出的各个接口

    ...

    },

    loaded: true, //模块的脚本是否执行完毕

    ...

    }

    以后用到这个模块时,就会到对象的exports属性中取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存中取值。

    CommonJS模块是加载时执行,即脚本代码在require时就全部执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,没有执行的部分不会输出。

    案例说明:

    //a.js

    exports.done = false;

    var b = require('./b.js');

    console.log('在a.js中,b.done = %j', b.done);

    exports.done = true;

    console.log('a.js执行完毕!')

    //b.js

    exports.done = false;

    var a = require('./a.js');

    console.log('在b.js中,a.done = %j', a.done);

    exports.done = true;

    console.log('b.js执行完毕!')

    //main.js

    var a = require('./a.js');

    var b = require('./b.js');

    console.log('在main.js中,a.done = %j, b.done = %j', a.done, b.done);

    输出结果如下:

    //node环境下运行main.js

    node main.js

    在b.js中,a.done = false

    b.js执行完毕!

    在a.js中,b.done = true

    a.js执行完毕!

    在main.js中,a.done = true, b.done = true

    JS代码执行顺序如下:

    1)main.js中先加载a.js,a脚本先输出done变量,值为false,然后加载b脚本,a的代码停止执行,等待b脚本执行完成后,才会继续往下执行。

    2)b.js执行到第二行会去加载a.js,这时发生循环加载,系统会去a.js模块对应对象的exports属性取值,因为a.js没执行完,从exports属性只能取回已经执行的部分,未执行的部分不返回,所以取回的值并不是最后的值。

    3)a.js已执行的代码只有一行,exports.done = false;所以对于b.js来说,require a.js只输出了一个变量done,值为false。往下执行console.log('在b.js中,a.done = %j', a.done);控制台打印出:

    在b.js中,a.done = false

    4)b.js继续往下执行,done变量设置为true,console.log('b.js执行完毕!'),等到全部执行完毕,将执行权交还给a.js。此时控制台输出:

    b.js执行完毕!

    5)执行权交给a.js后,a.js接着往下执行,执行console.log('在a.js中,b.done = %j', b.done);控制台打印出:

    在a.js中,b.done = true

    6)a.js继续执行,变量done设置为true,直到a.js执行完毕。

    a.js执行完毕!

    7)main.js中第二行不会再次执行b.js,直接输出缓存结果。最后控制台输出:

    在main.js中,a.done = true, b.done = true

    总结:

    1)在b.js中,a.js没有执行完毕,只执行了第一行,所以循环加载中,只输出已执行的部分。

    2)main.js第二行不会再次执行,而是输出缓存b.js的执行结果。exports.done = true;

    2. ES6模块的循环加载

    ES6模块与CommonJS有本质区别,ES6模块是动态引用,遇到模块加载命令import时不会去执行模块,只是生成一个指向被加载模块的引用,需要开发者保证真正取值时能够取到值,只要引用是存在的,代码就能执行。

    案例说明:

    //even.js

    import {odd} from './odd';

    var counter = 0;

    export function even(n){

    counter ++;

    console.log(counter);

    return n == 0 || odd(n-1);

    }

    //odd.js

    import {even} from './even.js';

    export function odd(n){

    return n != 0 && even(n-1);

    }

    //index.js

    import * as m from './even.js';

    var x = m.even(5);

    console.log(x);

    var y = m.even(4);

    console.log(y);

    执行index.js,输出结果如下:

    babel-node index.js

    1

    2

    3

    false

    4

    5

    6

    true

    可以看出counter的值是累加的,ES6是动态引用。如果上面的引用改为CommonJS代码,会报错,因为在odd.js里,even.js代码并没有执行。

    //改用CommonJS规范加载文件,执行会报错

    var x = m.even(5);

    ^

    TypeError: m.even is not a function

    at Object. (/Users/zourong/Projects/node/ES6/mainx.1.js:3:11)

    at Module._compile (internal/modules/cjs/loader.js:689:30)

    3. 总结

    1)CommonJS模块是加载时执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,没有执行的部分不会输出。

    2)ES6模块是动态引用,遇到模块加载命令import时不会去执行模块,只是生成一个指向被加载模块的引用。

    CommonJS模块规范主要适用于后端Node.js,后端Node.js是同步模块加载,所以在模块循环引入时模块已经执行完毕。推荐前端工程中使用ES6的模块规范,通过安装Babel转码插件支持ES6模块引入的语法。

    页面内容主要来源于《ES6标准入门》Module 这一章的介绍。如果有描述不清楚或错误的地方,欢迎留言指证。

    参考资料:

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    展开全文
  • 1.背景介绍ES6是ECMAScript2015的常用叫法。ES6在2015年发布,所以也被称之为 ES2015ES2016指 2016年发布ES2017指 2017年发布ES2018指 2018年发布ES2019指 2019年发布ES2012指 20120年发布一直以为ES6就是2016年发布...
  • Generator Function(生成器函数)是 ES6 引入的新特性,该特性早就出现在了 Python、C#等其他语言中。 A generator is a function that can stop midwayand then continue from where it stopped. Generator 函数是...
  • 模块化概述在传统前端开发中并没有模块化这样的开发规范,因此传统的开发模式面临着两个主要问题:命名冲突问题(多个JS文件之间如果存在同名的变量,则会发生变量覆盖问题)文件依赖问题(JS文件之间无法实现相互的...
  • With the use of transpilers it is already possible to use ES6 modules. One of the easiest ways is using Browserify and Babelify.The problem I'm having is how to handle dependency management.In the old...
  • 异步代码的执行主要依赖宿主环境的事件循环机制。 先通过一段伪代码了解一下事件循环: //callbackQueue是一个用作队列的数组//(先进,先出) var callbackQueue = []; //execStack是一个用作栈的数组//(后进,先出...
  • 作为参考,请查看从这里开始的两条消息:https://esdiscuss.org/topic/how-to-solve-this-basic-es6-module-circular-dependency-problem#content-21解决方案如下所示:// --- Module Aimport C,{initC} from './c';...
  • 但是它们是如何管理这些依赖的、它们之间有什么区别,如果出现了循环依赖应该怎么解决。在回答上面几个问题之前,先让我们了解下语义化版本规则。语义化版本使用第三方依赖时,通常需要指定依赖的版本范围,比如...
  • 在使用es6的export时,我们可能时常遇到这样的疑问:export default const x = 9; 为啥会报语法错误?export default function f(){}; export default class C{}; 为啥这又可以呢?文件a:export let x = 8; 文件b:...
  • "循环加载"(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。// a.jsvar b = require('b');// b.jsvar a = require('a');通常,"循环加载"表示存在强耦合,如果处理不好,还可能导致...
  • 说到前端模块化,就不得不说到循环加载,就像混乱背后隐藏着的清晰地秩序。什么叫循环加载?我们来看一段代码。12345678910111213const b = require('./b');b();module.exports = function(){console.log('This is a...
  • 1 2 3 4 5 [es6]-13-Iterator和for...of循环6 7 8 /*9 * Iterator遍历器的概念10 * 遍历器是一种接口,为各种不同的数据结构提供统一的访问机制。任何11 * 数据结构只要部署Iterator接口,就可以完成遍历操作。12 *...
  • } JavaScript的for循环 如果你只想要原生的JavaScript,而不想依赖于jQuery或Lodash这样的外部库,那这个小技巧是非常有用的。 普通写法: for (let i = 0; i ; i++) 速写: for (let index in allImgs) Array....
  • 编者按:ECMAScript 6已经正式发布了,作为它最重要的方言,Javascript也即将迎来语法上的重大变革,InfoQ特开设“深入浅出ES6”专栏,来看一下ES6将给我们带来哪些新内容。本专栏文章来自Mozilla Web开发者博客,由...
  • 目录let / const 解构赋值箭头函数模板字符串async / await (ES 2017)模块导入 / 导出以上排名不分...比如下面这段代码:我们运行一下,控制台的输出结果会是这样:我们可以看到,在 for 循环中定义的变量 i,我...
  • 新能源汽车特别是纯电动汽车,作为未来汽车的发展方向,...我们不妨比较两个品牌的两款价格相近、具有代表性的纯电动汽车:比亚迪的唐EV与蔚来的ES6,让它们来说说话,看看谁才是市场的真正王者。品牌号召力孰高孰低...
  • 一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of 循环在每次迭代后会返回一个形式为[key,value]的数组 基本使用语法 new Map([iterable]) Iterable 可以是一个数组或者其他 iterable 对象,...
  • 一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of 循环在每次迭代后会返回一个形式为[key,value]的数组 基本使用 语法 new Map([iterable]) Iterable 可以是一个数组或者其他 iterable 对象...
  • "循环引用"(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。 通常,”循环引用"表示存在强耦合,如果处理不好,还可能导致递归加载,使得程序无法执行,因此应该避免出现。 ...
  • es6 循环加载ES6模块

    2018-02-05 20:19:52
    循环加载ES6模块 “循环加载”(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。 // a.jsvar b = require('b');// b.jsvar a = require('a'); 通常,“循环加载”表示存在强...
  • // foo.js const bar = require('./bar.js'); console.log('value of bar:', bar); module.exports = 'This is foo.js'; // bar.js const foo = require('./foo.js');...console.log('value of foo:', foo);...

空空如也

空空如也

1 2 3 4 5
收藏数 98
精华内容 39
关键字:

es6依赖循环