电竞比分网-中国电竞赛事及体育赛事平台

分享

ES6塊級(jí)作用域

 quasiceo 2017-07-25

前面的話

  過去,javascript缺乏塊級(jí)作用域,var聲明時(shí)的聲明提升、屬性變量等行為讓人困惑。ES6的新語法可以幫助我們更好地控制作用域。本文將詳細(xì)介紹ES6新引入的塊級(jí)作用域綁定機(jī)制、let和const聲明機(jī)制及最佳實(shí)踐

 

var聲明

【變量提升】

  var聲明會(huì)發(fā)生”變量提升“現(xiàn)象,即變量可以在聲明之前使用,值為undefined 

復(fù)制代碼
function getValue(condition){
    if(condition){
        var value = 'blue';
        return value;
    }else{
     //此處可訪問變量value,值為undefined
return null; }
    //此處可訪問變量value,值為undefined }
復(fù)制代碼

  如果沒有javascript開發(fā)經(jīng)驗(yàn),可能會(huì)認(rèn)為只有condition為true時(shí),才會(huì)創(chuàng)建變量value

  但實(shí)際上,在預(yù)編譯階段,javascript引擎會(huì)將上面的函數(shù)修改成下面這樣

復(fù)制代碼
function getValue(condition){
    var value;
    if(condition){
        value = 'blue';
        return value;
    }else{
        return null;
    }
}
復(fù)制代碼

  變量value的聲明被提升到函數(shù)頂部,而初始化操作依然留在原處。如果不注意,很可能引起錯(cuò)誤。為些,ES6引入了塊級(jí)作用域來強(qiáng)化對(duì)變量生命周期的控制

【塊級(jí)聲明】

  塊級(jí)聲明用于聲明在指定塊的作用域之外無法訪問的變量,它存在于

  1、函數(shù)內(nèi)部

  2、{}之間的塊區(qū)域內(nèi)

 

let聲明

  let聲明的用法與var聲明相同。用let代替var來聲明變量,就可以把變量的作用域限制在當(dāng)前代碼塊中

復(fù)制代碼
function getValue(condition){
    if(condition){
        let value = 'blue';
        return value;
    }else{
         //變量value在此處不存在
        return null;
    }
    //變量value在此處不存在
}
復(fù)制代碼

  變量value改由關(guān)鍵字let進(jìn)行聲明后,不再被提升到函數(shù)頂部。執(zhí)行流離開if塊時(shí),value立刻被銷毀。如果condition的值為false,就永遠(yuǎn)不會(huì)聲明并初始化value

【禁止重聲明】

  假設(shè)作用域中已經(jīng)存在某個(gè)標(biāo)識(shí)符,此時(shí)再使用let關(guān)鍵字聲明它就會(huì)拋出錯(cuò)誤

var count = 30;
//拋出語法錯(cuò)誤
//Uncaught SyntaxError: Identifier 'count' has already been declared
let count = 40;

 

const聲明

  使用const聲明的是常量,其值一旦被設(shè)定后不可更改。因此,每個(gè)通過const聲明的常量必須進(jìn)行初始化

const num = 30;
//拋出語法錯(cuò)誤
//Uncaught SyntaxError: Missing initializer in const declaration
const name;

  const與let聲明老師塊級(jí)標(biāo)識(shí)符,所以常量也只在當(dāng)前代碼塊中有效,一旦執(zhí)行到塊外會(huì)立即被銷毀。常量同樣也不會(huì)被提升到作用域頂部

if(condition){
    const num = 30;    
}
//此處無法訪問num

【禁止重聲明】

  與let類似,在同一作用域內(nèi)用const聲明已經(jīng)存在的標(biāo)識(shí)符也會(huì)導(dǎo)致語法錯(cuò)誤,無論該標(biāo)識(shí)符是使用var,還是let聲明的

復(fù)制代碼
var message = 'hello';
let num = 10;

//這兩條語句都會(huì)拋出錯(cuò)誤
const message = "goobye";
const num = 30;
復(fù)制代碼

【無法再賦值】

  const與let聲明最大的不同之處在于,const聲明的常量無法再賦值

復(fù)制代碼
let num1 = 10;
num1= 20;

const num2 = 10;
//Uncaught TypeError: Assignment to constant variable.
num2 = 20;
復(fù)制代碼

【可修改對(duì)象屬性】

  const聲明不允許修改綁定,但允許修改值。這也就意味著用const聲明對(duì)象后,可以修改該對(duì)象的屬性值

復(fù)制代碼
const person = {
    name: 'huochai'
};
//可以修改對(duì)象屬性的值
person.name = 'match';
//Object {name: "match"}
console.log(person);

//拋出語法錯(cuò)誤
//Uncaught TypeError: Assignment to constant variable.
person = {
    name: 'match'
}
復(fù)制代碼

 

臨時(shí)死區(qū)

  與var不同,let和const聲明的變量不會(huì)被提升到作用域頂部,如果在聲明之前訪問這些變量,會(huì)引發(fā)錯(cuò)誤。而從作用域頂部到聲明變量語句之前的這個(gè)區(qū)域,被稱為臨時(shí)死區(qū)(temporal dead zone),簡(jiǎn)稱為TDZ

復(fù)制代碼
if(true){
    //undefined
    console.log(typeof value);
    var value = "blue";
}

if(true){
    //Uncaught ReferenceError: value is not defined
    console.log(typeof value);
    let value = "blue";
}
復(fù)制代碼

  但是,在let或const聲明的作用域之外使用該變量就不會(huì)報(bào)錯(cuò)

//undefined
console.log(typeof value);
if(true){
    let value = "blue";
}

 

循環(huán)綁定

【var聲明】

  長(zhǎng)久以來,var聲明使得在循環(huán)中創(chuàng)建函數(shù)異常困難,因?yàn)樽兞康搅搜h(huán)之外仍能訪問

復(fù)制代碼
var funcs = [];
for(var i = 0; i < 10; i++){
    funcs.push(function(){
        //輸出10次10
        console.log(i);
    });
}
funcs.forEach(function(func){
    func();
})
復(fù)制代碼

  上面代碼中,預(yù)期的結(jié)果是輸出數(shù)字0-9,但它卻一連串輸出了10次10,這是因?yàn)檠h(huán)里的每次迭代同時(shí)共享著變量i,循環(huán)內(nèi)部創(chuàng)建的函數(shù)全都保留了對(duì)相同變量的引用,循環(huán)結(jié)束時(shí)變量i的值為10,所以每次調(diào)用console.log(i)時(shí)就會(huì)輸出10

【IIFE】

  為解決這個(gè)問題,可以在循環(huán)中使用立即調(diào)用函數(shù)表達(dá)式(IIFE),以強(qiáng)制生成計(jì)數(shù)器變量的副本

復(fù)制代碼
var funcs = [];
for(var i = 0; i < 10; i++){
    funcs.push((function(value){
        return function(){
            //0
            //1
            //...
            //9
            console.log(value);
        }
    })(i));
}
funcs.forEach(function(func){
    func();
})
復(fù)制代碼

  在循環(huán)內(nèi)部,IIFE表達(dá)式為接受的每一個(gè)變量i都創(chuàng)建了一個(gè)副本并存儲(chǔ)為變量value,這個(gè)變量的值就是相應(yīng)迭代創(chuàng)建的函數(shù)所使用的值,因此調(diào)用每個(gè)函數(shù)都會(huì)像從0-9循環(huán)一樣得到期望的值

【let】

  let聲明模仿上例中IIFE所做的一切來簡(jiǎn)化循環(huán)過程。每次迭代循環(huán)都會(huì)創(chuàng)建一個(gè)新變量,并以之前迭代中同名變量的值將其初始化

復(fù)制代碼
var funcs = [];
for(let i = 0; i < 10; i++){
    funcs.push(function(){
        //0
        //1
        //...
        //9
        console.log(i);
    });
}
funcs.forEach(function(func){
    func();
})
復(fù)制代碼

  以上這段循環(huán)相比之下更為簡(jiǎn)潔,每次循環(huán)時(shí)let聲明都會(huì)創(chuàng)建一個(gè)新變量i,并將其初始化為i的當(dāng)前值,所以循環(huán)內(nèi)部創(chuàng)建的每個(gè)函數(shù)都能得到屬性它們自己的i的副本

  對(duì)于for-in循環(huán)和for-of循環(huán)來說也是一樣的

復(fù)制代碼
var funcs = [];
obj = {
    a:true,
    b:true,
    c:true
}
for(let key in obj){
    funcs.push(function(){
        //a
        //b
        //c
        console.log(key);
    })
}
funcs.forEach(function(func){
    func();
})
復(fù)制代碼

【const】

  對(duì)于const聲明來說,由于其無法改變變量的值,所以無法使用普通的for循環(huán)

復(fù)制代碼
var funcs = [];
for(const i = 0; i < 10; i++){
    funcs.push(function(){
            //Uncaught TypeError: Assignment to constant variable.
        console.log(i);
    });
}
funcs.forEach(function(func){
    func();
})
復(fù)制代碼

  由于for-in循環(huán)中每次迭代不會(huì)修改已有綁定,而是創(chuàng)建一個(gè)新綁定,所以在for-in循環(huán)中可以使用const

復(fù)制代碼
var funcs = [];
obj = {
    a:true,
    b:true,
    c:true
}
for(const key in obj){
    funcs.push(function(){
        //a
        //b
        //c
        console.log(key);
    })
}
funcs.forEach(function(func){
    func();
})
復(fù)制代碼

 

屬性變量

  對(duì)var聲明的變量來說,如果處于全局作用域,它們會(huì)自動(dòng)成為window對(duì)象的屬性。這意味著用var很可能無意中覆蓋一個(gè)已經(jīng)存在的全局變量

//function RegExp() { [native code] }
console.log(RegExp);
var RegExp = "hello";
console.log(RegExp);//'hello'
console.log(window.RegExp);//'hello'

  如果使用let或const聲明的變量,不會(huì)成為window對(duì)象的屬性

let RegExp = "hello";
console.log(RegExp);//'hello'
console.log(window.RegExp);//function RegExp() { [native code] }

  因此,如果希望在window對(duì)象下定義變量,要使用var聲明。如果不希望,則使得let或const

 

最佳實(shí)踐

  默認(rèn)使用const,只有確實(shí)需要改變變量的值時(shí)使用let

  因?yàn)榇蟛糠肿兞康闹翟诔跏蓟蟛粦?yīng)再改變,而預(yù)料外的變量值的改變是很多bug的源頭

 

 

好的代碼像粥一樣,都是用時(shí)間熬出來的

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多