• var/let/const、块级作用域、TDZ、变量提升


    概览

    ES6 新增了两个定义变量的关键字:letconst,它们几乎取代了 ES5 定义变量的方式:varlet是新的var,const简单的常量声明。

    function f() {
      {
        let x;
        {
          // okay, block scoped name
          const x = "sneaky";
          // error, const
          x = "foo";
        }
        // error, already declared in block
        let x = "inner";
      }
    }
    

    letconst实现块级作用域

    let,const创建的变量都是块级作用域:它们只存在包围它们的最深代码块中。

    function func() {
        if (true) {
            let tmp = 123;
            // const tmp = 123;
        }
        console.log(tmp); // ReferenceError: tmp is not defined
    }
    console.log(tmp);// ReferenceError: tmp is not defined
    

    相比之下,var声明的是函数域。

    function func() {
        if (true) {
            var tmp = 123;
        }
        console.log(tmp); // 123
    }
    func()
    console.log(tmp); // tmp is not defined
    

    面试题:循环中定时器闭包

    for(var i = 0; i < 5; i++) {
      setTimeout(() => {
        console.log(i) //5, 5, 5, 5, 5
      }, 0)
    }
    console.log(i) //5 i跳出循环体污染外部函数
    
    //将var改成let之后
    for(let i = 0; i < 5; i++) {
      setTimeout(() => {
        console.log(i) // 0,1,2,3,4
      }, 0)
    }
    console.log(i)//i is not defined i无法污染外部函数
    

    在for循环中使用var声明的循环变量,会跳出循环体污染当前的函数。

    letconst暂时性死区(temporal dead zone)

    let,const声明的变量拥有暂时性死区:当进入它的作用域,它不能被访问(获取或设置)直到执行到达声明。
    简单描述:

    if (true) {
      //这块区域是TDZ
      console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization
      let a = 1
      // const a = 1
    }
    
    if (true) { // enter new scope, TDZ starts
        // Uninitialized binding for `tmp` is created
    
        tmp = 'abc'; // ReferenceError
        console.log(tmp); // ReferenceError
    
        let tmp; // TDZ ends, `tmp` is initialized with `undefined`
        console.log(tmp); // undefined
    
        tmp = 123;
        console.log(tmp); // 123
    }
    

    下面示例将演示死区(dead zone)是真正短暂时间的(基于时间)和不受空间条件限制(基于位置)

    if (true) { // enter new scope, TDZ starts
        const func = function () {
            console.log(myVar); // OK!
        };
    
        // Here we are within the TDZ and
        // accessing `myVar` would cause a `ReferenceError`
    
        let myVar = 3; // TDZ ends
        func(); // called outside TDZ
    }
    

    var变量提升

    JavaScript中,我们通常说的作用域是函数作用域,使用var声明的变量,无论是在代码的哪个地方声明的,都会提升到当前作用域的最顶部,这种行为叫做变量提升(Hoisting)

    下面代码,演示了函数的变量提升:

    { // Enter a new scope
    
        console.log(foo()); // hello, due to hoisting
        function foo() {
            return 'hello';
        }
    }
    

    也就是说,如果在函数内部声明的变量,都会被提升到函数开头,而在全局的声明,就会提升到全局作用域的顶部。

    function test() {
        console.log('1: ', a) //undefined
        if (false) {
          var a = 1
        }
        console.log('3: ', a) //undefined
    }
    
    test()
    

    实际执行时,上面的代码中的变量a会提升到函数顶部声明,即使if语句的条件是false,也一样不影响a的提升。

    function test() {
        var a
        //a声明没有赋值
        console.log('1: ', a) //undefined
        if (false) {
          a = 1
        }
        //a声明没有赋值
        console.log('3: ', a) //undefined
    }
    

    在嵌套函数的情况,变量只会提升到最近一个函数的顶部,而不会到外部函数。

    //b提升到函数a顶部,但不会提升到函数test。
    function test() {
        function a() {
          if (false) {
            var b = 2
          }
        }
        console.log('b: ', b)
    }
    
    test() //b is not defined
    

    let不允许重复声明

    let不允许在相同作用域内,重复声明同一个变量。

    // 报错
    function func() {
      let a = 10;
      var a = 1;
    }
    
    // 报错
    function func() {
      let a = 10;
      let a = 1;
    }
    

    因此在函数内部不能重新声明函数

    function func(arg) {
      let arg;
    }
    func() // 报错 Identifier 'arg' has already been declared
    
    function func(arg) {
      {
        let arg;
      }
    }
    func() // 不报错
    
    

    const命令

    一般使用场景:

    const start = 'hi all';
    
    const getName = () => {
      return 'jelly';
    };
    
    const conf = {
      fav: 'Coding'
    };
    
    // 模板
    const msg = `${start}, my name is ${getName()}, ${conf.fav} is my favourite`;
    

    你可能不知道的事:

    // 1. 与引号混用
    const wantToSay = `I'm a "tbfed"`;
    
    // 2. 支持多行文本
    const slogan = 
    `
    I have a dream today!
    `;
    
    // 比较适合写HTML
    const resultTpl = 
    `
      <section>
        <div>...</div>
      </section>
    `;
    

    varletconst有什么区别

    • 相同点var,let,const声明的变量,是不能被delete的;
    • 区别

    变量提升var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined;
    let,const不存在变量提升,即它们声明的变量一定要在声明后使用,否则会报错。

    暂时性死区var不存在暂时性死区;letconst存在暂时性死区,只有等声明变量后,才可以获取和使用该变量。

    重复声明var允许重复声明;latconst在同一作用域不允许重复声明。

    修改声明的变量varlet可以修改声明的变量;const声明一个只读常量,一旦声明,常量的值就不能改变。

    var/let/const、块级作用域、TDZ、变量提升

  • 相关阅读:
    博弈论--sg函数
    博弈论--nim博弈
    dp--bitset优化
    树--欧拉序
    树--dfs序
    树--dsu on tree
    树--树的重心
    单调队列
    单调栈
    dp--背包
  • 原文地址:https://www.cnblogs.com/yihan123/p/14598648.html
Copyright © 2020-2023  润新知