• 技术文章 >web前端 >js教程

    es6块级绑定中let and const的详细分析

    不言不言2019-03-30 09:43:18转载792

    本篇文章给大家带来的内容是关于es6块级绑定中let and const的详细分析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

    变量声明一直是js工作中最微妙的一部分,它不像C语言一样,变量总是在被它创建的时候声明,js语言可以允许你在你需要声明变量的时候进行声明。

    var let const 之变量声明

    var 声明与变量提升。

    当我们使用var关键字进行变量声明的时候,无论变量声明的位置在哪里,都会被是为声明于所在的函数的顶部(如果不在函数内的话,则视为在全局作用域的顶部)这就是所谓的变量提升(hoisting)

    var提升如下:

    function getValue(condition) {
    if (condition) {
    var value = "blue";
    // 其他代码
    return value;
    } else {
    // value 在此处可访问,值为 undefined
    return null;
    }
    // value 在此处可访问,值为 undefined
    }

    块级声明let

    块级声明也就是让所声明的变量在指定的作用域外无法被访问到,块级作用域在如下情况下被创建

    1. 在一个函数内部,
    2. 在一个代码块(由一对花括号包裹)内部

    let声明的语法和var声明一致,由于let声明不会将变量提升到函数顶部,因此我们需要手动将let声明放置到顶部,以便让变量在整个代码块内部可用。

    如下所示:

    function getValue(condition) {
    if (condition) {
    let value = "blue";
    // 其他代码
    return value;
    } else {
    // value 在此处不可用
    return null;
    }
    // value 在此处不可用
    }

    禁止重复标识

    如果一个标识符已经在代码内部被定义,重复进行let声明会报错

    var a = 30;
    //报错
    let a = 30;

    a变量被声明了两次:一次使用 var ,另一次使用 let 。因为 let 不能在同一作用域内重复声明一个已有标识符,此处的 let 声明就会抛出错误。另一方面,在嵌套的作用域内使用 let 声明一个同名的新变量,则不会抛出错误,以下代码对此进行了演示:

    var count = 30;
    // 不会抛出错误
    if (condition) {
    let count = 40;
    // 其他代码
    }

    这段代码中不会抛错,关键点在于let在同一级代码块中重复声明会报错

    const常量声明

    在es6中可以使用const语法进行声明。使用const声明的变量会被认为是常量(constant),意味着他们的值在被设置完成后既不能再被改变。正因为如此,所有的const的变量都需要在声明时进行初始化,

    // 有效的常量
    const maxItems = 30;
    // 语法错误:未进行初始化
    const name;

    maxItems 变量被初始化了,因此它的 const 声明能正常起效。而 name 变量没有被初始化,导致在试图运行这段代码时抛出了错误。const声明会组织对变量绑定和对自生值的修改,这意味着const声明并不会组织对变量成员的修改。例如:

    const person = {
    name: "Nicholas"
    };
    // 工作正常
    person.name = "Greg";
    // 抛出错误
    person = {
    name: "Greg"
    };

    const声明和let声明的对比

    1. 首先他们都是块级声明,这就意味着常量在声明它们的语句块外是无法被访问的,并且声明也不会被提升,示例如下:

    if (condition) {
    const maxItems = 5;
    // 其他代码
    }
    // maxItems 在此处无法访问
    1. 它们在统一级作用域中重复声明时会导致抛出错误

    暂时性死区

    当我们使用let或者const 进行声明的时候,在到达声明处之前都是无法访问的,如果我们试图访问会导致一个引用错误。出项这个问题是因为暂时性死区

    当JS 引擎检视接下来的代码块并发现变量声明时,它会在面对 var 的情况下将声明提升到函数或全局作用域的顶部,而面对 let 或 const 时会将声明放在暂时性死区内。任何在暂时性死区内访问变量的企图都会导致“运行时”错误(runtime error)。只有执行到变量的声明语句时,该变量才会从暂时性死区内被移除并可以安全使用。

    循环中的块级绑定

    for (var i = 0; i < 10; i++) {
    process(items[i]);
    }
    // i 在此处仍然可被访问
    console.log(i); // 10

    输出的结果并不是预期的值而是10;是因为var声明导致的变量的提升。聪明的你肯定会想到使用块级绑定来进行变量声明

    for (let i = 0; i < 10; i++) {
    process(items[i]);
    }
    
    console.log(i);

    i在此处是不是会正常输出呢,其实不会,在这个例子中会导致报错,为什么呢?因为i在此处不可访问。本例中的变量 i 仅在 for 循环内部可用,一旦循环结束,该变量在任意位置都不可访问。

    我们在来看看一下代码

    var funcs = [];
    for (var i = 0; i < 10; i++) {
    funcs.push(function() { console.log(i); });
    }
    funcs.forEach(function(func) {
    func(); // 输出数值 "10" 十次
    });

    你原本可能预期这段代码会输出 0 到 9 的数值,但它却在同一行将数值 10 输出了十次。这是因为变量 i 在循环的每次迭代中都被共享了,意味着循环内创建的那些函数都拥有对于同一变量的引用。在循环结束后,变量 i 的值会是 10 ,因此当 console.log(i) 被调用时,
    每次都打印出 10 。

    为了修正这个问题,开发者在循环内使用立即调用函数表达式(IIFEs),以便在每次迭代中强制创建变量的一个新副本,示例如下:

    var funcs = [];
    for (var i = 0; i < 10; i++) {
    funcs.push((function(value) {
    return function() {
    console.log(value);
    }
    }(i)));
    }
    funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
    });

    循环内的 let 声明

    let 声明通过有效模仿上例中 IIFE 的作用而简化了循环。在每次迭代中,都会创建一个新的
    同名变量并对其进行初始化。这意味着你可以完全省略 IIFE 而获得预期的结果,就像这样

    var funcs = [];
    for (let i = 0; i < 10; i++) {
    funcs.push(function() {
    console.log(i);
    });
    }
    funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
    })

    我们是否会想到这个问题:为什么同样的代码使用let声明会导致不一样的结果呢?
    在循环中let声明每次都创建了一个新的i变量,因此在循环内部创建的函数获得了各自的i副本,而每个i副本的值都会在每次的循环迭代声明变量的时候确定了

    var funcs = [],
    object = {
    a: true,
    b: true,
    c: true
    };
    for (let key in object) {
    funcs.push(function() {
    console.log(key);
    });
    }
    funcs.forEach(function(func) {
    func(); // 依次输出 "a"、 "b"、 "c"
    });

    本例中的 for-in 循环体现出了与 for 循环相同的行为。每次循环,一个新的 key 变量绑定就被创建,因此每个函数都能够拥有它自身的 key 变量副本,结果每个函数都输出了一个不同的值。而如果使用 var 来声明 key ,则所有函数都只会输出 "c" 。
    let 声明在循环内部的行为是在规范中特别定义的,而与不提升变量声明的特征没有必然联系。事实上,在早期 let 的实现中并没有这种行为,它是后来才添加的。

    循环内的常量声明

    虽然es6没有明确的规范我们不能在for循环中使用const声明,然而它会根据循环方式的不同而有不同的行为,我们可以在初始化时使用const,但是当循环试图改变变量的值的时候会抛出错误,例如:

    var funcs = [];
    // 在一次迭代后抛出错误
    for (const i = 0; i < 10; i++) {
    funcs.push(function() {
    console.log(i);
    });
    }

    在此代码中, i 被声明为一个常量。循环的第一次迭代成功执行,此时 i 的值为 0 。在
    i++ 执行时,一个错误会被抛出,因为该语句试图更改常量的值。因此,在循环中你只能使
    用 const 来声明一个不会被更改的变量
    而另一方面, const 变量在 for-in 或 for-of 循环中使用时,与 let 变量效果相同。因
    此下面代码不会导致出错:

    var funcs = [],
    object = {
    a: true,
    b: true,
    c: true
    };
    // 不会导致错误
    for (const key in object) {
    funcs.push(function() {
    console.log(key);
    });
    }
    funcs.forEach(function(func) {
    func(); // 依次输出 "a"、 "b"、 "c"
    });

    这段代码与“循环内的 let 声明”小节的第二个例子几乎完全一样,唯一的区别是 key 的值在
    循环内不能被更改。 const 能够在 for-in 与 for-of 循环内工作,是因为循环为每次迭
    代创建了一个新的变量绑定,而不是试图去修改已绑定的变量的值(就像使用了 for 而不是
    for-in 的上个例子那样)。

    全局块级绑定

    let 与 const 不同于 var 的另一个方面是在全局作用域上的表现。当在全局作用域上使用 var 时,它会创建一个新的全局变量,并成为全局对象(在浏览器中是 window )的一
    个属性。

    总结

    let和const块级作用域的引入,能够使我们减少很多无心的错误,它们的一个副作用,是不能在变量声明位置之前访问它们

    块级绑定当前的最佳实践就是:在默认情况下使用 const ,而只在你知道变量值需要被更改的情况下才使用 let 。这在代码中能确保基本层次的不可变性,有助于防止某些类型的错误。

    本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript视频教程栏目!

    以上就是es6块级绑定中let and const的详细分析的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除
    专题推荐:javascript
    上一篇:vue keep-alive组件的使用以及原理介绍 下一篇:ES6中Generator函数的用法介绍
    大前端线上培训班

    相关文章推荐

    • ES6生成器用法介绍(附示例)• es6中filter() 数组过滤方法的介绍(附代码)• es6-promise源码的分析• ES6中新的变量声明方式介绍(附代码)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网