zoukankan      html  css  js  c++  java
  • var和let 等总结

    1: 输出全是6
    for
    (var i = 1;i < 6;i ++) { setTimeout(function fn() { console.log(i) },i * 1000) }
    一下两种方式正常
    for
    (var i = 1;i < 6;i ++) { // 闭包 (function(arg){ setTimeout(function fn() { console.log(arg) },arg * 1000) })(i);//调用时参数 } for (let i = 1;i < 6;i ++) { // es6 setTimeout(function fn() { console.log(i) },i * 1000) }

    先看var,let的作用域不同

    var是函数作用域,let是块作用域。

    在函数中声明了var,整个函数内都是有效的,比如说在for循环内定义的一个var变量,实际上其在for循环以外也是可以访问的

    而let由于是块作用域,所以如果在块作用域内定义的变量,比如说在for循环内,在其外面是不可被访问的,所以for循环推荐用let

    2、let不能在定义之前访问该变量,但是var可以。

    let必须先声明,在使用。而var先使用后声明也行,只不过直接使用但没有定义的时候,其值是undefined。var有一个变量提升的过程,当整个函数作用域被创建的时候,实际上var定义的变量都会被创建,并且如果此时没有初始化的话,则默认为初始化一个undefined

     3、let不能被重新定义,但是var是可以的

     

    2:异步函数执行顺序问题

    上边第一次for循环 输出全部为6的原因

     导致这个问题的原因还得从JavaScript的底层性质说起,总所周知JavaScript的一大特点就是单线程,那我们是如何处理多线程和异步的情况呢?利用的就是任务队列这一思想,学过node都知道的事件循环机制。

    任务队列

    在JavaScript中我们拥有两个任务队列,分别为宏任务队列(macro-task)和微任务队列(micro-task),新标准中又被称为 task 与 jobs。

     process.nextTick() 指在当前"执行栈"的尾部之后,下一次Event Loop(主线程读取"任务队列")之前 ,触发process指定的回调函数。也就是说,它指定的任务总是发生在所有异步任务之前,当前主线程的末尾。(nextTick虽然也会异步执行,但是不会给其他io事件执行的任何机会)

    整个事件循环是从整体代码执行为第一个宏任务开始的,然后再执行其中的微任务,完成第一次循环,再找到接下来的宏任务执行,再执行其微任务,这样一层层的循环直到执行完毕。

    接下来我们分析一下上面那道问题,首先执行第一次宏任务,也就是 for 循环,执行5遍,分别得到:

    setTimeout(function fn() {
          console.log(i)  // 异步里面的函数是未执行状态,所以i是现在是不确定的
      },1000);
    
    setTimeout(function fn() {
          console.log(i)
      },2000)
    
    ... // 5个待执行函数

    上诉五个待执行函数将排进宏队列中,第一遍宏队列执行完毕,由于没有微队列,我们接下来继续执行其宏队列。

    一秒后,将执行第一次 console.log(i) ,根据JavaScript中作用域的定义,首先我们会在当前执行作用域寻找 i 的值,发现不存在,这时候向上寻找在全局作用域中,我们找到了 i 这时候 i 已经是6了,所以控制台将打印出6,后续相同,隔一秒打印出一个6。

    现在明白了为什么会出现5个6了吧,也应该明白了,为什么能通过闭包解决这个问题,因为相当于给每个宏任务都添加上 i 的值,就不需要向上查找全局的 i 了。

    拓展

    我们根据上表来做做练习,看看你是否真的掌握了。

    // 请判断下列代码的执行顺序
    console.log("start");
    
    // 因为setImmediate只在node环境中可用,所以这里用setTimeout(0)代替
    setTimeout(function() {
      console.log("setImmediate");
    }, 0);
    
    setTimeout(function() {
      console.log("timeout1");
      new Promise(function(resolve) {
        resolve();
        console.log("timeout1_promise");
      }).then(function() {
        console.log("timeout1_then");
      });
    }, 2000);
    
    for (var i = 1; i < 6; i++) {
      setTimeout(function() {
        console.log("loop" + i);
      }, i * 1000);
      console.log(i);
    }
    
    function xhr() {
      // 用async模拟XHR
      console.log("xhr");
      return "async";
    }
    
    async function asyncFn() {
      // 用async模拟
      const tag = await xhr();
      return tag;
    }
    
    asyncFn().then(arg => console.log(arg));
    
    new Promise(function(resolve) {
      console.log("promise1");
      reject();
    }).catch(function() {
      console.log("catch1");
    });
    
    setTimeout(function() {
      console.log("timeout2");
      new Promise(function(resolve) {
        console.log("timeout2_promise");
        resolve();
      }).then(function() {
        console.log("timeout2_then");
      });
    }, 1000);

    先自己默默地做一下,再对比控制台中的输出,错了不要紧,我们一步步来看:

    第一次事件循环

     这时候控制台将输出:start1,2,3,4,5,xhr,promise1,catch1,async; 解释一下,async为Generator 函数的语法糖(es6新特性),它是一个状态机,封装了多个内部状态,与一般的异步不太一样,虽然都是同时执行微任务,但它会有一个挂起,再.next()的过程,会延迟一点。

    第二次事件循环

    这时候控制台将输出:setImmediateloop6,timeout2,timeout2_promise,timeout2_then,timeout1,timeout1_promise,timeout1_then,loop6,loop6,loop6,loop6

    附:

    下面再给出一开始for循环i问题的其他方法

    // 利用setTimeout的第三个参数
    for (var i=1; i<6; i++) {
      setTimeout( function fn(i) {
        console.log(i);    
       }, i*1000,i );
    }
    
    
    // 利用bind函数
    for (var i=1; i<6; i++) {
      setTimeout( function fn(i) {
        console.log(i);
      }.bind(null,i), i*1000 );
    }

    本文来自两篇知乎文章汇总而成

    https://zhuanlan.zhihu.com/p/265002815

    https://zhuanlan.zhihu.com/p/71157926

    附:

    JS引擎的两大特点:单线程和非阻塞

    单线程

    JS引擎是基于单线程(Single-threaded)事件循环的概念构建的。同一时刻只运行一个代码块在执行,与之相反的是像JAVA和C++一样的语言,它们允许多个不同的代码块同时执行。对于基于线程的软件而言,当多个代码块同时访问并改变状态时,程序很难维护并保证状态不出错。

    非阻塞

    非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。非阻塞是通过事件循环机制实现的。
    JS通常是非阻塞的,除了某些特殊情况,JS会停止代码执行:
    - alert, confirm, prompt(除了Opera)
    - “页面上的程序正忙”的系统对话框弹出

    技术交流qq群:143280841
  • 相关阅读:
    输出菱形
    for语句输出三角形
    阶乘倒数的和
    变量赋值判断奇偶
    928作业
    各种主流数据库的比较(所以说我觉得Oracle这个keng?入的不错?)
    Oracle创建表
    (转载)全球唯一标识GUID
    (转载)Java基础知识总结
    两天以来对plsqldev操作的记忆
  • 原文地址:https://www.cnblogs.com/zuochanzi/p/14464773.html
Copyright © 2011-2022 走看看