閱讀699 返回首頁    go 阿裏雲 go 技術社區[雲棲]


es6(2)——let和const

let

解釋:

1. 簡單來說,就是類似var,但使用該方法聲明的變量,隻在當前作用域生效;

幾個特點:

1、let和var相比,不存在變量提升(即先使用後聲明會報錯);

{
    console.log(a);    //Uncaught ReferenceError: a is not defined
    let a = 1;
}

2、使用let聲明的變量,當前作用域裏,該變量唯一。

即,假如在當前作用於的父級作用域裏聲明一個var a,在當前作用域裏也聲明一個let a,那麼在當前作用域裏,隻有let聲明的a生效,也就是說,以下代碼是不可行的:

var a = 1;
{
    console.log(a);    //Uncaught ReferenceError: a is not defined
    let a = 2;
}

但若將let a改為var a,那麼console.log的結果就是1了(采用父級作用域的變量的值);

var a = 1;
{
    console.log(a);    //1
    var a = 2;
}

3、同一級作用域裏麵,隻允許對一個變量使用一個let來進行聲明。

具體來說,2個var a是正常的,剩下的2個let a或者先let a後var a又或者先var a再let a是統統都是報錯的。

{
    var a = 1;
    var a = 2;    //ok
}
{
    var a = 1;    //Uncaught SyntaxError: Identifier 'a' has already been declared
    let a = 2;
}
{
    let a = 1;
    let a = 2;    //Uncaught SyntaxError: Identifier 'a' has already been declared
}
{
    let a = 1;
    var a = 2;    //Uncaught SyntaxError: Identifier 'a' has already been declared
}

4、但是不同級作用域裏,是沒有影響的,比如分別在父級作用域和當前作用域裏聲明let a各一次,是沒問題的。

另外,外層和內層作用域都聲明了一個同樣的變量名時,內層作用域該變量的值的修改,對外層作用域的值是沒有影響的。

{
    let a = 1;
    {
        let a = 2;    //can do it
    }
    console.log(a);  //1
}

但若內層不適用let聲明,而是直接調用a = 2進行修改,那麼是有影響的

{
    let a = 1;
    {
        a = 2;    //1 to 2
    }
    console.log(a);  //2
}

5、let的限製情況

不是十分確認,是實踐後的結果。(部分因為條件不足,沒法調試)

  1. 變量名和和頁麵裏html標簽的id名重複時,該變量在全局作用域下。變量聲明時不能用let,隻能用var(在safari瀏覽器下會發生此bug);
  2. 解決方法是比如把變量名放在局部作用於內;
  3. 有時候let需要在"use strict"條件下才能使用(嚴格模式),我在寫nodejs的服務器端遇見過這種問題;

塊級作用域

1、let相當於新增了塊級作用域。

簡單來說,以前隻有全局作用域和函數作用域,例如以下是函數作用域的體現:

(function () {
    var a = 1;
})();
console.log(a);    //Uncaught SyntaxError: Unexpected identifier

而在使用var時,是不存在塊級作用域的,即如下代碼視為同一個作用域內,所以console.log可以顯示結果:

{
    var a = 1;
}
console.log(a);    //1

而使用let時,會相當於創造出了一個塊級作用域,例如將以上代碼改用let進行聲明,則在塊級作用域外無法正常顯示結果:

{
    let a = 1;
}
console.log(a);    //Uncaught ReferenceError: a is not defined

【Babel處理】另外提一句,使用babel在對以上代碼進行轉換處理時,為了使結果符合預期的運行結果,他會自動進行一些處理。

例如將let a轉換為其他的,例如var _a,而下麵的console.log(a)不變;如果有重名(比如有_a)他會繼續處理,變為_a2這樣。

2、在塊級作用域下進行函數聲明

簡單來說,let製造了塊級作用域(見上麵);

而ES5中(也許還包括更早的),理論上,函數隻能在全局作用域和函數作用域內聲明,不能在塊級作用域內聲明。但實際上,因為要兼容以前版本,所以是可以的(不會出錯,除非在嚴格模式'use strict')。

但也是因為這樣,所以如果在塊級作用域內聲明函數,你很難控製在不同瀏覽器中(包括同一瀏覽器不同版本)的實現是同樣的效果。所以應該 盡量避免在塊級作用域內聲明函數

3、do表達式(存疑)

按照阮一峰的博客關於do表達式的說明,現在有一個提案,使得塊級作用域可以變為表達式,也就是說可以返回值,辦法就是在塊級作用域之前加上do,使它變為do表達式。

代碼如下:

let x = do {    //Uncaught SyntaxError: Unexpected token do
  let t = f();
  t * t + 1;
};

我實測無效(chrome版本 55.0.2883.87),會報錯,報錯信息見注釋,不知為何。也許是該提案未實現?


const

解釋:

1. 簡單來說,學過c++的可以理解為c++的const,沒學過可以繼續往下看;
2. 如果指向非按引用傳遞類型(比如字符串,布爾值等),那麼該值聲明後無法被修改;
3. 如果指向按引用傳遞,則無法更改其指向的對象,但該對象的值可以被修改;
4. 準確的說,是讓按引用傳遞時,保證該const變量指向的地址不變(而非該地址裏的數據不變)(理解本條需要有指針相關概念);

1、指向非按引用傳遞類型的變量,其變量值不可以被修改

即聲明後不能被修改,修改會報錯;

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

2、指向引用類型的變量,其值可以被修改,但是不能讓其指向另外一個對象

對象的值可以被修改:

const a = {test: 1};
a.test = 2;
console.log(a.test);    //2

不能修改指向的對象:(報錯這步是因為更改了指向的對象)

var a = {test: 1};
var b = {another: 2};
const c = a;
console.log(c);    //{test:1}
c = b;    //Uncaught TypeError: Assignment to constant variable.

3、不能聲明const變量時不賦值

會報錯

const a;    //Uncaught SyntaxError: Missing initializer in const declaration

4、塊級作用域,相關特性類似let

顯然是塊級的

var a = 1;
{
    const a = 2;
    console.log(a);    //2
}
console.log(a);    //1

不存在變量提升,出現暫時性死區,不能先使用後聲明

{
    console.log(a);    //Uncaught ReferenceError: a is not defined
    const a = 1;
}

也不可重複聲明(在同一個塊級作用域內)(使用let和var同樣不可)

{
    const a = 1;
    const a = 2;    //Uncaught SyntaxError: Identifier 'a' has already been declared
}

5、指向一個被凍結的對象

const和Object.freeze不同,後者是凍結對象,而前者隻涉及地址。

所以可以二者結合起來,讓const變量指向一個被凍結的對象。那麼該變量則不可更改指向的目標(因為const)也不可更改其值(因為凍結)。

先從阮一峰的博客拿來一個深度凍結函數(遞歸凍結該對象所有屬性):

var constantize = (obj) => {
    Object.freeze(obj);
    Object.keys(obj).forEach((key, value) => {
        if (typeof obj[key] === 'object') {
            constantize(obj[key]);
        }
    });
};

然後略微修改,讓const變量指向一個被凍結的對象,
會發現既無法更改變量裏對象的值,也無法讓變量指向另外一個對象。
有點像讓const變量成為一個常量。
(下麵代碼沒有體現深度凍結的效果)

var constantize = (obj) => {
    Object.freeze(obj);
    Object.keys(obj).forEach((key, value) => {
        if (typeof obj[key] === 'object') {
            constantize(obj[key]);
        }
    });
    return obj;     //I add this code
};
const a = constantize({a: 1});
console.log(a);    //{a:1}
a.a = 2;
console.log(a.a);    //1
a = 10;    //Uncaught TypeError: Assignment to constant variable.

頂層對象的屬性

  1. 所謂頂層對象,在js裏麵指window
  2. 當一個變量在頂層作用域裏(比如說打開瀏覽器通過F12的console來直接輸入命令),那麼該變量在之前情況下,是屬於window這個頂層對象的屬性的;
  3. 我們之前一般稱之為全局變量,全局變量在以前會被認為就是window的屬性的值;
  4. 而ES6中則不是,全局變量和頂層對象的屬性的值將脫鉤;

具體來說:

1、通過var或者function甚至直接寫變量名然後進行賦值創建的對象,其變量名作為key添加到window對象中,而window裏該key的值為被賦值的值。

如代碼:

console.log(window.a);  //undefined
console.log(window.b);  //undefined
console.log(window.c);  //undefined
var a = 1;
console.log(window.a);  //1
b = 2;
console.log(window.b);  //2
function c(){}
console.log(window.c);  //function c(){}

2、而通過let、const,以及之後的class創建的對象,則不會被添加到window裏麵。

如代碼:

console.log(window.a);  //undefined
console.log(window.b);  //undefined
let a = 1;
console.log(window.a);  //undefined
const b = 2;
console.log(window.b);  //undefined

頂層對象的獲得

  1. 簡單來說,頂層對象在瀏覽器裏就是window;但是在Node.js裏麵沒有window(Web Worker也沒有,他是運行在後台的js腳本);
  2. 瀏覽器和Web Worker裏,self指向頂層對象,但是Node.js裏沒有self;
  3. Node裏,頂層對象是global,但其他環境不支持(比如chrome裏打global會告訴你未定義);
  4. 有時候我們需要用同一套代碼,但在各個環境拿到頂層對象(啥時候?),所以得找個通用的辦法;
  5. 但是沒有非常完美的。
  6. 阮一峰給了兩個辦法,我直接摘抄了,如下代碼:
// 方法一
(typeof window !== 'undefined'
    ? window
    : (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
    ? global
    : this);

// 方法二
var getGlobal = function () {
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
};

想要了解更多的話,參考阮一峰的博客相關內容

最後更新:2017-06-12 12:01:58

  上一篇:go  企業網站建設中外鏈發布有哪幾種方式?
  下一篇:go  【幹貨】iOS 11 短信攔截功能初探