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
相關文章
相關標籤/搜索