for(var i = 0;i<5;i++){
        setTimeout(() => {
            console.log(i); //  5 5 5 5 5
        }, 0);
    //你可能以为它会打印0 1 2 3 4
    //实际上会输出 5 5 5 5 5

  为了能够更加直观的看出执行的过程,将代码改造成一下过程:

    for(var i = 0;i<5;i++){
        console.time("计时器一");
        setTimeout(() => {
            console.time("计时器二");
            console.log(i);
            console.timeEnd("计时器二");
        }, 0);
        console.timeEnd("计时器一");

  可以直观的看到,计时器一执行完了才去执行计时器二,在执行计时器二的时候,i已经是5了。
  之所以会这样是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时逻辑时,所有的i都是同一个变量,因为输出的都是同一个最终值。
  而在使用let声明迭代变量时,JavaScript引擎在后台会为每个迭代循环声明一个新的迭代变量。而每个setTimeout引用的都是不同的变量实例,所以输出的时我们期望的值,也就是循环过程中每个迭代变量的值:

    for(let i = 0;i<5;i++){
        setTimeout(() => {
            console.log(i); //0 1 2 3 4 
        }, 0);
    for(let i = 0;i<5;i++){
        console.time("计时器一");
        setTimeout(() => {
            console.time("计时器二");
            console.log(i);
            console.timeEnd("计时器二");
        }, 0);
        console.timeEnd("计时器一");

  这种每次迭代声明一个独立变量实例的行为适用于所有风格的for循环,包括for infor of

小建议:
  尽量少或者不使用var去声明,使用let有助于提升代码质量,因为变量有了明确的作用域、声明位置。

文章大多表述文字源于:红宝书第四版28-29页,少部分表述为个人,表达不到位请原谅

数组的循环 说到数组,我们先考虑到的肯定是循环,其中,ES5就有很多的数组循环方法,早就出现了。只是我们还没掌握,下面我就详细介绍一下。 //以下方法都可以接收三个参数,分别是当前值,当前下标,和当前数组(接收的参数是一致的,只是方法不一样) 1.arr.forEach() // forEach 方法其实就是来替换普通的for循环的,他是一个回调函数,每循环一次,回调一次。 let arr =...
在ES5当中使用for循环都是采用var,而在ES6中都是采用的let,并且我们更推荐于let,这是为何? 因为var是在js语言设计者Brendan Eich设计的一种缺陷,这种缺陷不能更改,并且作者在十年前就提出了修复此缺陷. 在进行讲解的时候,小伙伴们需要知道在js语言中,if和for是没有作用域的,只有function才拥有,var是全局变量,let是块级作用域,const是常量(不可改变的变量称为常量);可以把let看做成更完美的var //ES5中使用for循环点击事件 //bug:每次点击.
在理解var、let、const三者在声明变量的区别时,遇到了一道十分有意思的题:&lt;body&gt; &lt;ul&gt; &lt;li&gt;第一个li&lt;/li&gt; &lt;li&gt;第二个li&lt;/li&gt; &lt;li&gt;第三个li&lt;/li&gt; &lt;li&gt;第四个li&lt;/li&gt;
for (let i = 1; i <= 5; i++) { setTimeout ( function timer() { console.log(i) // 1,2,3,4,5 }, i * 1000) 由于let的存在,使得for循环内形成块级作用域。每次迭代都会产生一个块级作用域,这个作用域记录此次i的值。 timer函数包含有对外部定义作用域中变量i的引用,因此是闭包函数。当它每次回调时,访问的作用域是每次迭代生成的块级作用域,i的值在该作用域中已被记
因为let有自己的作用域块,所以在for循环表达式中使用let其实就等价于在代码块中使用let,也就是说 for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域 for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次。 var liList = document.querySelectorAll('li') // 共
在项目开发的时候后台的返回的数据结构不能满足于checkBox单个选择的条件,当我选择一个时会把其他的数据也一直选过来,这里就需要自己去重构一个独一无二的key来进行for循环选择: 如以下数据: tableDate: [ { groupNum: "360120", groupName: "仙剑奇侠传" }, { groupNum: "4542120", gro...
利用闭包将每一次循环中的 i 保存起来,等待延时器回调函数获取。 for(var i = 0; i < 6; i++){ setTimeout(function generatorFunc (i) { return function sonFunc () { console.log(i) }(i), 1000) generatorFunc 是一个立即执行函数,接受一个参数并返回一个函数 sonFunc 。sonFunc 作为延时器的回调函数。 const 声明一个只读的常量,一旦声明,常量的值就不能改变。 在 ES6 之前,JavaScript 使用var声明变量只有两种作用域: 全局变量 与 函数内的局部变量。 var声明变量使用 var声明变量只有两种作用域: 全局变量 与 函数内的局部变量。 全局变量 在函数外声明变量作用域是全局的, 在任意地方都可以引用
初学JavaScript时,都会听前辈建议不要用var声明变量,es6以后要用let,道理是啥也没当回事,直到for循环时候才发现循环条件for(var i=0;i<arr.length;i++)中的i是一个i,这就导致做点击事件的时候,无论谁点击,结果i都是退出循环后最终的i固定不变。其实这个问题在C#中压根不是问题........最后查了下,再加上技术群大佬们的指点,终于理解了原因。接下来就举例子给大家详细说明下。 开门见山,先说核心结论。若不理解请继续往后读,相信读完全文再回头看你一定会深有体
for(let i = 0; i < btn.length; i ++){ console.log(i); btn[i].addEventListener('click',function(){ console.log(i); 疑问1.为什么可以每次打印打印循环中i的值而不是循环结束后i的值 理解:因为es6存在块作用域,而let声明变量是在当前作用域块的顶端,因为每一次循环都会生成一个作用域块,所 1. 创建一个函数,命名为generateCode(),用于生成随机六位验证码。 2. 在函数内部,创建一个空字符串变量code,用于存储生成的验证码。 3. 使用for循环从0到5,循环六次,每次生成一个随机数字。 4. 在每次循环中,使用Math.random()方法生成一个0到1之间的随机小数。 5. 将这个随机小数乘以10,得到一个0到10之间的随机整数。 6. 使用Math.floor()方法将这个随机整数向下取整,得到一个0到9之间的随机整数。 7. 将这个随机整数转换为字符串,并将其添加到code变量中。 8. 循环结束后,将code返回作为生成的验证码。 下面是一个示例代码实现: ```javascript function generateCode() { let code = ""; for (let i = 0; i < 6; i++) { let random = Math.floor(Math.random() * 10); code += random.toString(); return code; console.log(generateCode()); 以上代码将会生成一个随机的六位验证码,并通过console.log()在控制台输出。你可以运行代码来查看生成的结果。