let与const

let与const

ES2015(ES6)新增长了两个重要的JavaScript关键字: letconstjavascript

块级做用域

代码块内若是存在let或者const,代码块会对这些命令声明的变量从块的开始就造成一个封闭做用域。java

{
    let a = 1;
    var b = 2;
    function s(){return a;}
    console.dir(s);
    /*
      ...
      [[Scopes]]: Scopes[2]
        0: Block {a: 1}
        1: Global ...
    */
}
// 此处不能使用 a ,a 是块级做用域
// 此处可使用 b , b 在此处是全局做用域

[[Scopes]]是保存函数做用域链的对象,是函数的内部属性没法直接访问,[[Scopes]]中能够看到出现了一个Block块级做用域,这使得let特别适合在for中使用,在ECMAScript 2015引入let关键字以前,只有函数做用域和全局做用域,函数做用域中又能够继续嵌套函数做用域,在for并未具有局部做用域,因而有一个常见的闭包建立问题。git

function counter(){
    var arr = [];
    for(var i = 0 ; i < 3 ; ++i){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}

var coun = counter();
for(var i = 0 ; i < 3 ; ++i){
    console.log(coun[i]()); // 3 3 3
}

能够看到运行输出是3 3 3,而并非指望的0 1 2,缘由是这三个闭包在循环中被建立的时候,共享了同一个词法做用域,这个做用域因为存在一个ivar声明,因为变量提高,具备函数做用域,当执行闭包函数的时候,因为循环早已执行完毕,i已经被赋值为3,因此打印为3 3 3,可使用let关键字声明i来建立块级做用域解决这个问题es6

function counter(){
    var arr = [];
    for(let i = 0 ; i < 3 ; ++i){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}

var coun = counter();
for(var i = 0 ; i < 3 ; ++i){
    console.log(coun[i]()); // 0 1 2
}

固然也可使用匿名函数新建函数做用域来解决github

function counter(){
    var arr = [];
    for(var i = 0 ; i < 3 ; ++i){
        (function(i){
            arr[i] = function(){
                return i;
            }
        })(i);
    }
    return arr;
}

var coun = counter();
for(var i = 0 ; i < 3 ; ++i){
    console.log(coun[i]()); // 0 1 2
}

一次声明

同一做用域内letconst只能声明一次,var能够声明屡次数据结构

let a = 1;
let a = 1; //Uncaught SyntaxError: Identifier 'a' has already been declared

const b = 1;
const b = 1; //Uncaught SyntaxError: Identifier 'b' has already been declared

暂时性死区

当使用letconst生成块级做用域时,代码块会对这些命令声明的变量从块的开始就造成一个封闭做用域,代码块内,在声明变量以前使用它会报错,称为暂时性死区。闭包

{
    console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
    let a =1;
}

变量提高

letconst也存在变量提高,在ES6的文档中出现了var/let hoisting字样,也就是说官方文档说明letvar同样,都存在变量提高,可是与var的变量提高有所不一样函数

let 的「建立」过程被提高了,可是初始化没有提高。  
var 的「建立」和「初始化」都被提高了。  
function 的「建立」「初始化」和「赋值」都被提高了。

stackoverflow中比较有说服力的例子.net

x = "global";
// function scope:
(function() {
    x; // not "global"

    var/let/… x;
}());
// block scope (not for `var`s):
{
    x; // not "global"

    let/const/… x;
}

js中不管哪一种形式声明var,let,const,function,function*,class都会存在提高现象,不一样的是,var,function,function*的声明会在提高时进行初始化赋值为 undefined,所以访问这些变量的时候,不会报ReferenceError异常,而使用let,const,class声明的变量,被提高后不会被初始化,这些变量所处的状态被称为temporal dead zone,此时若是访问这些变量会抛出ReferenceError异常,看上去就像没被提高同样。指针

https://blog.csdn.net/jolab/article/details/82466362
https://www.jianshu.com/p/0f49c88cf169
https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6

window

在全局做用域中使用var直接声明变量或方法等会挂载到window对象上,letconst声明变量或方法等会保存在Script做用域中

var a = 1;
let b = 2;
const c = 3;

console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined
let a = 1;
{
    let b = 2;
     function s(){return a + b;}
     console.dir(s);
     /*
      ...
      [[Scopes]]: Scopes[3]
        0: Block {b: 2}
        1: Script {a: 1}
        2: Global ...
    */
}

初始化

varlet在声明时能够不赋初值,const必须赋初值

var a;
let b;
const c; //Uncaught SyntaxError: Missing initializer in const declaration

只读常量

const用以声明一个只读常量,初始化后值不可再修改

const a = 1;
a = 2; // Uncaught TypeError: Assignment to constant variable.

const其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不容许改动。对于简单类型numberstringbooleanSymbol,值就保存在变量指向的那个内存地址,所以const 声明的简单类型变量等同于常量。而复杂类型objectarrayfunction,变量指向的内存地址实际上是保存了一个指向实际数据的指针,因此const只能保证指针是固定的,至于指针指向的数据结构变不变就没法控制了。

const a = {};
console.log(a); // {}
a.s = function(){}
console.log(a); // {s: ƒ}

相关

ES6新特性 https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/ES6%E6%96%B0%E7%89%B9%E6%80%A7.md
Js变量提高 https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/JS%E5%8F%98%E9%87%8F%E6%8F%90%E5%8D%87.md
相关文章
相关标签/搜索