zoukankan      html  css  js  c++  java
  • Javascript中的var和let

    引子

    我们先来看一个常见的例子,

    function func(){  
      for (var  i = 0; i < 10; i++) {
          setTimeout(function(){
              console.log(i)
          },1000)
      }
    }
    func()

    输出的结果并不是我们预期的 0-9,而是输出了十个 10。而当我们把其中的 var 改成 let ,结果就成了 0-9。

    原因

    var 使用的是函数作用域,即 for 循环中的 var i 实际上在整个 func 函数中都有效。而 setTimeout 中的匿名函数(十个)都由于闭包的原因能够访问到同一个 i,因为他们是延迟执行的,所以等到 console.log(i) 被执行的时候, i 已经是 10 了。

    在以前我们面对这种情况常用的方法是通过另外一个函数来将 i 装进自己的函数作用域中:

    function helper(i){  
        setTimeout(function(){
            console.log(i)
        },1000)
    }
    function func(){  
      for (var  i = 0; i < 10; i++) {
          helper(i)
      }
    }
    func()

    这样虽然 setTimeout 中的匿名函数仍然是延时调用的,但是 helper 是即时调用的( helper(0), helper(1), ... ),且其参数 i 是其函数作用域持有的(而非 func持有),这样每个匿名函数通过闭包访问到的 i 都是独立的,所以能够得到 0-9 。

    为什么 let 不同

    let 是 ES6 引入的新特性,和 var 最大的不同在于,它是块级作用域的,所以相当于 for 的每次循环的 i 都是独立的,它们在语句块(所在的循环体)结束时就失效了(当然由于闭包的原因仍然能被 setTimeout 的匿名函数访问),匿名函数分别持有每个作用域的 i 而不是同一个。

    具体的差别

    上面虽然给了解释,但是仍然会有些模糊的感觉。下面来明确一下块级作用域和函数作用域的行为到底有什么不同:

    不同的作用域范围

    顾名思义,函数作用域在所在的函数都有用,而块级作用域则只在自己所在的代码块有用,举个例子:

    function func(){  
        if(true){
            var iAmVar = 'var'
            let iAmLet  = 'let'
        }
        console.log(iAmVar)  // 'var'
        console.log(iAmLet)  // 访问不到
    }
    func()

    变量提升的不同行为

    var 和 let 的变量提升行为也不同,首先了解什么叫变量提升,即

    var k = 'test'  
    function func(){  
      console.log(k)  // undefined
        var k = 'in'
    }
    func()

    即函数中的所有 var 的函数声明都会被提前到函数的头部,对于 var 来说,上面的代码相当于

    var k = 'test'  
    function func(){  
      var k
      console.log(k)  // undefined
        k = 'in'
    }
    func()

    而 let 的表现更为严格,同样的,let 的声明也会被提前,但是如果在这种情况下试图访问尚未初始化的 k ,会抛出一个 ReferenceError 。我们称这种特性为”临时死区”,这样能够避免一些奇奇怪怪的失误。

    另外,由于作用域的原因, let 的声明提前在代码块中就能生效。

    验证上面的想法

    所以,如何验证 for 循环每次执行时的 i 都不是同一个呢,也非常简单,我们把i 的声明放到 for 的外面:

    function func(){  
        let i
      for ( i = 0; i < 10; i++) {
          setTimeout(function(){
              console.log(i)
          },1000)
      }
    }
    func()

    再次得到了 10 个 10,和用 var 时一样(因为作用域都是 func 函数内)。

    总结

    let 的行为显得比 var 更加符合预期,规则也更为严格,所以我们应该尽可能的使用 let 和 const 来替代 var ,这样代码就不会出现开头那种奇怪的行为。

  • 相关阅读:
    android service
    Java 枚举7常见种用法
    SkylineGlobe Android 开发 面积计算示例代码
    SkylineGlobe 6.6 版本API更新
    SkylineGlobe 移动端开发测试
    SkylineGlobe 如何二次开发获取三维模型的BBOX和设置Tint属性
    SkylineGlobe 如何二次开发实现天际线分析功能
    SkylineGlobe API 如何以图层的方式导入MPT地形
    SkylineGlobe 如何使用二次开发接口创建粒子效果
    SkylineGlobe 支持火狐和谷歌浏览器的可运行示例代码
  • 原文地址:https://www.cnblogs.com/douglasvegas/p/5899394.html
Copyright © 2011-2022 走看看