V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
brooky
V2EX  ›  JavaScript

var 和 let 的区别?

  •  2
     
  •   brooky · 2016-11-15 23:19:31 +08:00 · 5352 次点击
    这是一个创建于 2909 天前的主题,其中的信息可能已经有所发展或是发生改变。
    var a = [];
    for (var i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 10   
    
    var a = [];
    for (let i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 6
    

    想不明白了, 谁能帮忙解释下这是为啥呢?

    31 条回复    2016-11-28 22:47:32 +08:00
    JohnLou
        1
    JohnLou  
       2016-11-15 23:29:29 +08:00
    我刚看了这个,因为 var 的作用域只能是函数和全局,而 let 是块,也就是{},所以对于 let , for 循环一次, i 就在那个循环里起作用而已。
    chemzqm
        2
    chemzqm  
       2016-11-15 23:32:43 +08:00
    你或许需要一本 exploring es6
    miketeam
        3
    miketeam  
       2016-11-15 23:38:03 +08:00 via iPhone
    总是喜欢把事情搞得这么复杂,所以一点都不喜欢了 js
    nanxiaobei
        4
    nanxiaobei  
       2016-11-15 23:58:22 +08:00
    用 var ,每个 function 都保存着 i 的同一个变量对象,为 10 。
    用 let ,是块级作用域,每次的 i 是独立的,大概是这样吧。
    brooky
        5
    brooky  
    OP
       2016-11-16 00:02:21 +08:00
    @JohnLou 用 var 声明的话数组里面存的是 [ funciton(){console.log(i)}, funciton(){console.log(i)}...]
    用 let 声明数组里面的即是[ function(){console.log("1")}, function(){console.log("2")} ....]?
    jswh
        6
    jswh  
       2016-11-16 00:09:00 +08:00
    brooky
        7
    brooky  
    OP
       2016-11-16 00:18:21 +08:00
    @jswh 我就是看到这个没有看明白的...
    JohnLou
        8
    JohnLou  
       2016-11-16 00:30:14 +08:00 via iPhone
    @brooky 不是很准确,可以理解为这样[{let i=0;}, {let i =1}], i ++之后就新起一个块。
    SoloCompany
        9
    SoloCompany  
       2016-11-16 01:08:54 +08:00
    你应该这样理解
    本来不应该有 var 和 let 的
    因为他们其实除了作用域以外没有任何区别
    但由于历史原因, var 的作用域泄露的问题被吐槽太多了,又不得不兼容,所以才产生了 let

    js 三种变量声明方式

    x;
    var x;
    let x;

    如果可以用更合适的关键字修饰符的话,就是等同于
    global x; (前提是变量没有在任何作用域中被定义)
    function local x;
    local x;
    ByZHkc3
        10
    ByZHkc3  
       2016-11-16 01:17:10 +08:00
    作用域的区别, let 只在当前循环或函数里有效,这等同于再函数里 var 了一样。
    brooky
        11
    brooky  
    OP
       2016-11-16 01:45:56 +08:00
    ````
    var tmp = new Date();

    function f() {
    console.log(tmp);
    if (false) {
    var tmp = "hello world";
    }
    }

    f(); // undefined
    ````
    又来个问题, 自学真的很不行是不是, 求老司机带带 付费指导
    baconrad
        12
    baconrad  
       2016-11-16 01:56:29 +08:00
    因為 var 的變量聲明會被提前,
    先這樣處理後, 再來看問題就會比較容易理解.


    ```
    var a = [];
    var i;
    for ( i = 0; i < 10; i++) {
    a[i] = function () {
    console.log(i);
    };
    }
    a[6](); // 10
    ```


    ```
    var tmp = new Date();

    function f() {
    var tmp;
    console.log(tmp);
    if (false) {
    tmp = "hello world";
    }
    }

    f(); // undefined

    ```
    geekaven
        13
    geekaven  
       2016-11-16 02:27:52 +08:00
    作用域,变量提升。
    geekaven
        14
    geekaven  
       2016-11-16 02:28:34 +08:00
    可以看看《你不知道的 javascript 》 第一卷 1 , 2 , 3 , 4 章
    Sivan
        15
    Sivan  
       2016-11-16 02:32:41 +08:00
    建议楼主先学一下基础知识再去学 ES6 相关改进。
    yyfearth
        16
    yyfearth  
       2016-11-16 06:58:34 +08:00
    @miketeam 其实不是复杂 是由于大量的历史包袱 JS 一开始设计的不好 然后修修改改 导致这样的问题
    yyfearth
        17
    yyfearth  
       2016-11-16 07:00:50 +08:00
    @brooky 所以两者不要混用
    let 是 block scope 和大多数语言一样 所以最近才出现 减少问题的发生
    var 是 function scope 由于历史原因 对于很多人来说 非常容易混淆

    所以你能用 let 的情况下 只用 let
    如果你要考虑浏览器兼容性的情况下 就只用 var
    不要混用 把自己搞糊涂
    kitalphaj
        18
    kitalphaj  
       2016-11-16 07:27:00 +08:00
    难道不是还有一个 const 么。。。。所以一共是四种修饰方式么? var, let, const, 和空。。。
    murmur
        20
    murmur  
       2016-11-16 08:05:13 +08:00
    目前 var 和 let 没任何区别 本来我以为 babel 会把 let 的变量都搞到函数里隔绝作用域 结果如果没重名的话就是把 var 改成 let
    如果有重名就在 let 的变量下家个下划线。。
    ryanzyy
        21
    ryanzyy  
       2016-11-16 08:48:36 +08:00
    for (var i = 0; i < 10; i++) console.log(i);
    i -> 10
    for (let i = 0; i < 10; i++) console.log(i);
    i -> undefined
    misaka19000
        23
    misaka19000  
       2016-11-16 09:09:26 +08:00
    let 这玩意是不是出自 lisp
    ssehacker
        24
    ssehacker  
       2016-11-16 09:22:35 +08:00
    13 楼正解,就这两点。
    4641585
        25
    4641585  
       2016-11-16 09:31:50 +08:00
    @murmur

    实际用下来感觉 var 的变量提升就是一个需要注意的坑,想不到除了作为面试题还有什么场合能用得上…所以 babe 这么做也没什么不对 233
    JohnLou
        26
    JohnLou  
       2016-11-16 11:31:16 +08:00
    补充一下我 8 楼说的,楼主是否明白了呢,执行下下面的代码。

    for(let i = 0;i < 3;i ++) {
    a[i] = function () {console.log(i);}
    setTimeout(function () {i = i * 1000}, 1000);
    }

    a[1]() // 1000
    a[2]() // 2000
    coldsnap
        27
    coldsnap  
       2016-11-16 11:31:33 +08:00
    @murmur
    你把楼主用 let 的语法用 babel 转了试试,通过新建一个 loop 函数的确给了 i 作用域,得到也是期望的结果;改名能避免变量提升带来的问题,这样做没问题。例如:
    { let a = 'a' } console.log(a)
    会被编译成
    { var _a = 'a' } console.log(a)
    你得到的结果和你在支持 let 语法的宿主环境中一样。

    而且有些情况根本不需要编译,例如写 electron 和 node 。准确来说你应该说对你没区别。
    SuperMild
        28
    SuperMild  
       2016-11-16 11:36:28 +08:00
    自从有了 let 之后,好像提倡不要用 var 了,绝大多数情况下用 let 就够了。如果一时之间觉得比较乱,建议不要去管 var ,只管用 let 就好了,没有必要执着于两者的区别(过一段时间回头看很可能会豁然开朗)。
    chnhyg
        29
    chnhyg  
       2016-11-16 11:47:51 +08:00
    var 与 let
    - let 是更完美的 var 。
    - let 声明的变量拥有块级作用域。
    - let 声明的全局变量不是全局对象的属性。
    - 形如 for(let I …) 的循环在每次迭代时都为 I 创建新的绑定。
    - let 声明的变量直到控制流到达该变量被定义的代码行时才会被装载,所以在到达之前使用该变量会触发错误。不可访问的这段时间变量一直处于作用域中,但是尚未装载,它们位于临时死区(Temporal Dead Zone)中。
    - 用 let 重定义变量会抛出一个语法错误。
    vlxer
        30
    vlxer  
       2016-11-16 14:54:20 +08:00
    要理解这个,首先要理解词法作用域。

    * 定义一个函数的时候,函数会保存当前作用域
    * 函数执行的时候,会创建一个新的作用域,用于存放函数参数和局部变量,同时新的作用域指向函数保存的作用域,构成作用域链

    因此,第一个例子中,由于没有块级作用域,循环里的所有函数都是共享同一个作用域的, a[0] == a[1] == ... == a[9] == i == 10 。

    第二个例子中,有块级作用域,等价于:

    ```js
    var a = [];
    for (var i = 0; i < 10; i++) {
    (function(i) {
    a[i] = function() {
    console.log(i);
    };
    })(i);
    }
    a[6](); // 6
    ```

    通过查看 babel 的转换也可以看出:
    http://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=latest%2Creact%2Cstage-2&experimental=false&loose=false&spec=false&code=var%20a%20%3D%20%5B%5D%3B%0Afor%20(let%20i%20%3D%200%3B%20i%20%3C%2010%3B%20i%2B%2B)%20%7B%0A%20%20a%5Bi%5D%20%3D%20function%20()%20%7B%0A%20%20%20%20console.log(i)%3B%0A%20%20%7D%3B%0A%7D%0Aa%5B6%5D()%3B%20%2F%2F%206&playground=true

    不明白问我,付费指导。
    anthozoan77
        31
    anthozoan77  
       2016-11-28 22:47:32 +08:00
    IIFE
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1100 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:30 · PVG 03:30 · LAX 12:30 · JFK 15:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.