美文网首页
还记得初学前端时,var 循环输出undefined的那个例子吗

还记得初学前端时,var 循环输出undefined的那个例子吗

作者: 天亮前被寻找的一只猫 | 来源:发表于2018-06-19 10:56 被阅读26次

    两年前刚接触前端时, 有这么一个例子.

    var message = [1,2,3];
    for(var i=0;i<message.length;i++){
    setTimeout(function(){
    console.log(message[i]);
    },i*1500)
    }
    按照当时的想法, 输出结果不肯定是 1 2 3吗?
    但是当,输出了 3个 undefined 时, 一脸懵逼的 我(or you) 是不是觉得 肯定是电脑huai了?哈哈

    那问题到底是出在了哪呢?

    看到好多借用这样的经典例子,去解释 var 的作用域.(其作用域是函数内)
    ps: 如果你不知道 (其作用域是函数内) 这句话的意思, 那么在你接触ES6的时候,肯定听过 代码块的 概念.
    感兴趣的 可以去了解一下 var ; let; const 三者的区别.

    当然,借助这个例子,也解释了 var 的作用域的相关知识, 但是 你真的了解了这段代码吗?
    反正我当时是不了解的! 单从作用域的角度,根本就解释不通! 反正不了解那就对了,因为很少有例子去提及 队列 线程!

    同样的上一段代码.

    var message = [1,2,3];
    for(var i=0;i<message.length;i++){
    console.log(message[i]);
    }
    这时候输出的 1 , 2 , 3 没毛病吧,也符合初学者的认知. 但是你想说,what? 为啥加个 setTimeout 就不行了呢?
    然后还扯到 什么var 作用域的问题?

    在这里, setTimeout 其实就是模拟了一个异步线程!异步线程!异步线程!

    for(var i=0;i<message.length;i++) {
    //异步操作
    }

    计算机的计算是每秒百万级的运算. 当for循环中的 i 判断 i< length , 然后++ 时, 异步操作 可能并没有执行完成,
    所以, i 继续++. 当然 当i++ 结束时, 异步操作也有可能是 没有结束的. (除非 你这个length的长度是几百万,循环的时间 花费 比异步操作还长)

    SO, 当终于轮到执行异步操作中的 输出语句 console.log(message[i]) 时. i的值,其实已经是 3.
    然后 执行了 3次 console.log(message[3]);

    为了 更形象的让大家理解, 我就举个例子, 括号中的时间 代表这个指令运行时的时间.
    执行队列就是:
    for 循环(0.1秒) ==>异步输出语句1(0+0.4秒时执行)
    for 循环 (0.2秒) ==>异步输出语句2(1.5秒+0.4秒时执行)
    for 循环 (0.3秒) ==>异步输出语句3(3秒+0.4秒时执行)
    异步 i=0时, setTimeout 延时 明明是 0 * 1.5 不应该是立即执行的吗? 然而,还是那句话,计算机的计算是每秒百万级的,同时计算机现在都是多核进行运算的, 所以, 至于哪个线程会 先执行完?计算机的线程是如何调配的? 这都是 是有很大偶然性滴!(开发中,考虑到性能优化的准则,有一项就是线程优化? 保持多少条线程异步执行才能更高效?感兴趣的请自行搜索相关知识)
    这是我的这段测试代码:
    var message = [1,2,3];
    for(i=0;i<message.length;i++){
    var start = new Date().getTime();//起始时间
    var end = new Date().getTime();//结束时间
    console.log("同步"+(end - start)+"ms");
    setTimeout(function(){
    var end1 = new Date().getTime();//结束时间
    console.log("异步"+(end1 - start)+"ms");
    },i*1500)
    }
    同步0ms
    同步0ms
    同步0ms
    异步1ms
    异步1502ms
    异步3005ms
    注意看这个执行顺序! 同步0ms ,异步1ms 这个就是所说的偶然性. 感兴趣的可以去深入了解下计算机线程; 栈 ;队列的概念, 以及 同步异步 的相关知识. 这不是今天要说的重点,所以就不过多阐述.

    言归正传: 既然 异步执行时, 我用了var 定义的变量. 那么for循环取值时 ,收到了影响.
    那有什么好的解决方法吗?
    答案肯定是有的啊!
    一: 使用forEach 代替 for循环
    messages.forEach(function (message, i) {
    setTimeout(function () {
    cat.say(message);
    }, i * 1500);
    });
    同样也输出了 1 , 2, 3

    二: 涉及ES6,使用 let 定义 的变量
    我们只需要将for循环中的 var i =0 ; 替换成 let i=0;
    for(let i=0;i<message.length;i++){
    setTimeout(function(){
    console.log(message[i]);
    },i*1500)
    }
    运算结果也是 1, 2, 3
    形如for (let x...)的循环在每次迭代时都为x创建新的绑定。let声明的变量直到控制流到达该变量被定义的代码行时才会被装载; 这里只说明了ES6的处理机制,深入的请自行学习.

    然后这时候 带着你的好奇与疑问 再去 学es6 相关知识点的时候,相信会有事半功倍的效果,也会加深你的理解.

    相关文章

      网友评论

          本文标题:还记得初学前端时,var 循环输出undefined的那个例子吗

          本文链接:https://www.haomeiwen.com/subject/gjxaeftx.html