• js中的变量提升


    ES6中为何要提出用let和const来定义变量?使用var会有什么弊端呢?其中最关键的就是:使用var关键字声明的变量,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部(如果声明不在任意函数内部,则视为在全局作用域的顶部)。这就是所谓的变量提升。

    栗子如下:

    function getValue(condition){
        if(condition){
            var value = 'blue';
            return value;
        }else{
            return null;
        }
    }

    实际上当condition为false或者true时候value都被创建。在else的语句中value的值也是可以被访问的,是undefined。

    ES6中引入块级声明:目的就是防止这种变量提升的现象。让声明的变量在指定的块级的作用域外无法被访问。创建形式如下:

    1,在一个函数内部

    2,由一对花括号包裹的代码块内部 

    function getValue(condition){
        if(condition){
            let value = 'blue';
            return value;
        }else{
            return null;
        }
    }

     使用let声明变量,该声明没有被提升到函数定义的顶部。因此value值if代码块之外无法访问,在condition为false时候,该变量永远不会被声明并初始化。

    使用let定义变量要注意禁止变量重复定义。

    常量声明:

    const进行变量的声明设置完成后就不允许再被更改,所以,所有的const变量都需要在声明时候进行初始化,不初始化会报错。

    let和const声明的对比:

    相同点:1,都是块级声明,不存在变量提升,2,如果变量在同一作用域中(无论是全局还是函数作用域)之前声明过,再用let或者const进行重复定义会报错。

    使用const声明对象:

    const person = {
        name:'miya'
    }
    
    person.name = 'jone'
    person
    Object {name: "jone"}

    Object {name: "jone"}
    person = {
        name:'chris'
    }
    VM183:1 Uncaught TypeError: Assignment to constant variable.(…)

    如果常量是一个对象,对象所包含的值是可以被修改的。但是尝试修改person对象本身就会报错。const阻止的是对于变量绑定的修改,而不阻止对成员值得修改。

    还有个相同点就是:暂时性死区

    使用let或者const声明变量时候,在未达到声明位置之前都是无法访问的,试图访问会导致引用错误。

    js引擎在处理var时将声明提升到函数或者全局作用域的顶部,而在处理let或者cosnt时,则会将声明放入暂时性死区。只有执行到变量的声明语句时,该变量才会从暂时性死区内被移除,才可以安全使用。

    暂时性死区是块级绑定的一个独特表现,而另一个独特表现就是在循环内使用。

    循环中的块级绑定:

    for(var i = 0;i<10;i++){
        setTimeout(function(){
            console.log(i)
        },0)
    }
    console.log(`i==>${i}`)

    分析下这个代码的输出:肯定是 i==>10 然后输出10个10,因为使用var声明导致了变量提升,循环结束后i仍然可以被访问,但是如果使用let进行定义i,则在for循环的外部访问i是会报错的。

    循环内的函数:

    var的特点使得循环变量在循环作用于之外仍然可以被访问,于是在循环内部创建函数会不会出什么问题?思考入下:

    var funcs = [];
    for(var i = 0;i<10;i++){
        funcs.push(function(){console.log(i)});
    }
    funcs.forEach(function(func){
        func();
    })
    //10 times 10
    func()方法里面的i在循环的每次迭代中都被共享,所以在循环内部创建的那些函数都拥有对同一个变量的引用。循环结束后,变量i的值会是10。
    为了修正这个问题,开发者在循环内部使用立即调用函数表达式,在每次迭代中强制创建变量的一个新副本。例子如下:
    var func = [];
    
    for(var i = 0;i<10;i++){
        func.push((function(value){
          return function(){
              console.log(value);
          }
        }(i)))
    }
    
    func.forEach(function(func){
        func();
    })
    这种写法在循环内部使用了立即执行函数,变量i传递给立即执行函数,从而创建作为副本的value变量,每个value都存储每次迭代时变量i的值,迭代中的函数使用这些副本值,所以得到0-9的期望结果。
    循环内部let的使用:
    let可以简化像上面立即执行函数那样的作用,在每次迭代中,都会创建一个新的同名变量并对其进行初始化。
    而用const进行循环遍历会报错,因为第二次执行循环语句试图更改常量的值。因此在循环中只能使用const来声明一个确定不会被更改的变量。
    使用for/in for/of 进行遍历时候。看下面的栗子==>
    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"
    });
    这段代码没有报错。而且正常的输出结果,原因是:循环为每次迭代创建一个新的变量绑定,而不是像for循环那样去修改已绑定的变量的值。
    全局块级绑定:
    使用var进行全局变量声明时,该变量成为全局对象的一个属性(浏览器中是window),所以有可能无意中会覆盖一个已有的全局属性。
    像酱紫:
    var RegExp = 'hello';
    console.log(window.RegExp)
    可以看出window上面的RegExp对象被覆盖,而如果在全局使用let或者const,虽然在全局作用域会创建新的绑定,但是不会有任何的属性被添加到全局对象上面。
    最佳实践===>
    默认情况下应当使用let来代替var。let的行为方式是var本应有的方式,因此直接用let替代var更符合逻辑。而需要受到保护的变量再使用const。
    而更加流行的方式可以酱紫:默认情况下使用const,仅当明确变量值需要被更改的情况下才使用let,因为大部分变量在初始化后都应当保持不变。

    总结:

    let和const将块级作用域引入js中,都不会进行变量提升,不能在变量声明位置之前访问,因为块级绑定存在暂时性死区,试图在声明之前访问就会导致错误。

    let和const在循环中的差异,let和const都能在每次迭代时创建一个新的变量绑定。

    块级绑定当前的最佳实践是:默认情况使用const,而仅在变量值需要给更改的时候才使用let,有助于防止某些类型的错误。 

  • 相关阅读:
    1337:【例3-2】单词查找树
    1336:【例3-1】找树根和孩子
    1301:大盗阿福
    CSP-J/S 第一轮知识点选讲
    【转】C++STL各容器的操作及复杂度
    如何查看SUSE的版本信息
    野人和传教士过河问题的C语言源代码
    ubuntu如何安装软件
    Heavy Transportation POJ
    Heavy Transportation POJ
  • 原文地址:https://www.cnblogs.com/tangjiao/p/9163826.html
Copyright © 2020-2023  润新知