Var聲明和提升機(jī)制
在函數(shù)作用域和全局作用域中通過(guò)關(guān)鍵字var聲明變量,無(wú)論是在哪里聲明,都會(huì)被當(dāng)成在當(dāng)前作用頂部聲明的變量,這就是我們常說(shuō)的變量提升機(jī)制。廢話不多說(shuō),來(lái)看一個(gè)例子:
function getValue(flag){
if(flag){
var value = 'abc';
retun value;
}else{
console.log(value);//undefined;
return null;
}
}
由上面的例子可以看出,變量value由于變量提升機(jī)制,在哪里都可以被訪問(wèn)到,其實(shí)上面代被解析成一下代碼:
function getValue(flag){
var value;//變量提升
if(flag){
value = 'abc';
retun value;
}else{
console.log(value);//undefined;
return null;
}
}
塊級(jí)聲明
塊級(jí)聲明用于聲明在指定塊的作用域之外無(wú)法訪問(wèn)的變量;塊級(jí)作用域存在于:
- 函數(shù)內(nèi)部
- 代碼塊{}中
let聲明
ES6新增let聲明,用法類似var。用let代替var來(lái)聲明變量,可以將變量的作用域限制在當(dāng)前代碼塊中。
var聲明變量:
var a = 10;
{
var a = 20;
}
console.log(a);//20
let聲明變量:
let a = 10;
{
let a = 20;
}
console.log(a);//10
let也沒(méi)有變量提升機(jī)制,用let聲明的變量不會(huì)在被到作用域頂部;
function getValue(flag){
if(flag){
let value = 'abc';
return value;
}else{
console.log(value);//error:value is not defined
return null;
}
}
運(yùn)行上面代碼,當(dāng)flag為false時(shí),會(huì)報(bào)錯(cuò);
const聲明
用const聲明一個(gè)只讀的常量,一旦聲明,其值就不可以更改,并且聲明時(shí)必須初始化,否則會(huì)報(bào)錯(cuò);
const a ;//Module build failed: SyntaxError
const PI = 3.1415926;
console.log(PI);//3.1415926
PI = 3;//TypeError
const和let一樣都是塊級(jí)聲明,所以只在當(dāng)前作用域內(nèi)有用,一旦離開(kāi)就會(huì)報(bào)錯(cuò);
{
const a = 'abc';
}
console.log(a);//error
const的本質(zhì)
實(shí)際上const聲明保證的不是值的不改的,而是變量指向的內(nèi)存地址不得改動(dòng)。對(duì)于簡(jiǎn)單類型的數(shù)據(jù)(Number,String,Boolean),變量保存的就是其內(nèi)存地址,改變其值就是改變其內(nèi)存地址;對(duì)于復(fù)合類型的數(shù)據(jù)(Array,Object),變量保持的不是其內(nèi)存地址,而是指向
內(nèi)存地址的指針,所以復(fù)合類型的數(shù)據(jù)只要保證指針不改動(dòng)就行,至于里面的數(shù)據(jù)結(jié)構(gòu)改變,這個(gè)就不能控制了;
const obj = {};
obj.name = 'Jon';
obj.age = 23;
console.log(obj);//{name: "Jon", age: 23}
obj = {name:1};//error
從上面代碼中,常量obj是個(gè)對(duì)象,所以保存的是一個(gè)指針,當(dāng)增加屬性和刪除屬性時(shí),由于保存的指針沒(méi)有變,所以不會(huì)保存;當(dāng)給其重新賦值的時(shí)候,改變其指針,所以報(bào)錯(cuò);
禁止重復(fù)聲明
如果在同一作用域內(nèi),已經(jīng)存在一個(gè)變量,再使用let或const聲明這個(gè)變量時(shí)會(huì)報(bào)錯(cuò);
{
var a = 10;
let a = 10;//報(bào)錯(cuò)
}
在上面例子中,變量a在代碼塊中被聲明了兩次,第一次被var聲明,沒(méi)有任何問(wèn)題,當(dāng)?shù)诙伪籰et聲明時(shí),報(bào)錯(cuò);因?yàn)橐呀?jīng)存在變量a了;
let a = 10;
{
let a = 20;//不報(bào)錯(cuò)
}
上面的例子中,變量a被聲明兩次沒(méi)有報(bào)錯(cuò),因?yàn)檫@次聲明不在同一作用域;
暫時(shí)性死區(qū)(TDZ)
如果區(qū)塊中存在let和const命令,這個(gè)區(qū)塊對(duì)這些命令聲明的變量,從一開(kāi)始就形成了封閉作用域,且let和const聲明的變量不會(huì)被提升到作用域的頂部,如果在聲明之前使用這些變量,就會(huì)報(bào)錯(cuò)。
if(true){
console.log(tmp);//報(bào)錯(cuò)
let tmp = 'abc';
}
由于console.log(tmp);語(yǔ)句會(huì)拋出錯(cuò)誤,因此用let定義并初始化變量的語(yǔ)句不會(huì)執(zhí)行,此時(shí)就會(huì)稱為:“暫時(shí)性死區(qū)”(簡(jiǎn)稱:TDZ);所以在使用變量之前,都需要聲明變量;
全局作用域綁定
let和const與var的另一個(gè)區(qū)別是它們?cè)谌肿饔糜蛑械男袨?。?dāng)var被用于全局作用域時(shí),它會(huì)創(chuàng)建一個(gè)新的全局變量作為全局對(duì)象的屬性;
var str = 'Hello';
console.log(window.str);//Hello
如果在全局作用域中使用let或const,會(huì)在全局作用域下創(chuàng)建一個(gè)新的綁定,但該綁定不會(huì)添加為全局對(duì)象的屬性;
let str = 'Hello';
console.log(str);//Hello
console.log(window.str);//undefined