一、什么是變量
在讀這篇文章前,我們需要搞懂到底是什么變量,其實一句話就能概括:變量是一個可以保存任何數(shù)據(jù)類型值的命名占位符。
本篇文章將會介紹以下知識點:
什么是var、let、const
變量聲明規(guī)則
變量提升
塊作用域
暫時性死區(qū)
二、var關(guān)鍵字
2.1 初始化
初始化變量只需要在var
關(guān)鍵字后面跟變量名即可:
var a;
var b = 2;
上面這段代碼聲明了a和b兩個變量,a變量只是單純的聲明,并沒有賦值,js引擎遇到未賦值的變量時,會自動為其賦值一個值,這個值就是undefined
;變量b在聲明的時候我們給它賦值了一個數(shù)字2
,所以當(dāng)獲取變量b的時候,會得到一個number類型的2。
說到number類型,這里就不得不提到一個概念:Javascript中的變量屬于松散類型,也就是變量的值可以變更為任何數(shù)據(jù)類型的值,但并不建議這么做,隨意更改變量的數(shù)據(jù)類型會導(dǎo)致代碼可讀性降低。
// 翻譯后是var a = undefined;
var a;
var b = 2;
b = '2'; // 不建議這么做
在代碼開發(fā)過程中,有些場景需要創(chuàng)建多個變量,除了var a = 1; var b = 2;
這種寫法,還可以使用,
進行更便利的創(chuàng)建多個變量:
// 使用逗號創(chuàng)建多個變量
var a = 1,
b = 2,
c = 3;
// 等同于
var a = 1;
var b = 2;
var c = 3;
2.2 var聲明作用域
變量存在哪個作用域中是根據(jù)變量所聲明的場景位置決定,有兩種場景:全局、函數(shù)
。在全局作用域中聲明var變量,該變量在任何地方都可以被訪問到,而且還可以通過window.變量名
訪問。如果在函數(shù)內(nèi)部聲明變量,那么這個變量就只屬于這個函數(shù)自己的,外部訪問就會報錯,最后變量會在函數(shù)退出的時候被銷毀。
// 聲明全局變量
var a = 1;
// 訪問全局變量
console.log(a); // 1
console.log(window.a); // 1
// 聲明函數(shù)作用域內(nèi)的變量
function fun () {
var b = 2; // 只屬于fun函數(shù)的變量
console.log(b); // 2
};
fun(); // 2
console.log(b); // 報錯:b is not defined(變量b未定義)
2.3 var的變量提升
變量提升其實就是在初始化的時候,var變量會提升到當(dāng)前作用域最頂部,并且每一個變量的值都是默認(rèn)undefined,當(dāng)js引擎執(zhí)行到變量賦值的代碼后,變量的值才會是我們想要的那個值,根據(jù)這個理論,來看下面一段代碼,思考一下代碼執(zhí)行的結(jié)果是什么:
var a = 1,
b = 2;
function fun (a) {
console.log(b);
var b = 6;
var b = 5;
console.log(a);
console.log(b);
};
console.log(b);
fun(4);
console.log(b);
上面這段代碼其實并沒有復(fù)雜的交互,只是多次的訪問某些變量,會讓人有點眼花,沒關(guān)系,我們來一步一步的分解這些代碼,得到最終的結(jié)果,看是否和你的結(jié)果一樣:
全局作用域中出現(xiàn)了三個變量:
a、b、fun
(函數(shù)名也是一個變量)在函數(shù)執(zhí)行前打印了全局作用域中的變量b,所以得到的是
2
調(diào)用函數(shù)并傳入?yún)?shù)值,開始執(zhí)行函數(shù)內(nèi)部代碼
此時函數(shù)內(nèi)部變量a和b都提升到了函數(shù)作用域頂部,并且值都是undefined
變量a被賦值4
打印變量b,此時變量還沒有被賦值,所以得到的是
undefined
變量b開始被賦值,但遇到了兩個重名變量,最后一個變量會覆蓋之前的所有重名變量
獲取變量a,還記得嗎?在執(zhí)行參數(shù)的時候給變量a賦值了4,所以我們獲得的也是
4
因為之前的變量b被后面的變量b覆蓋了,所以現(xiàn)在獲取變量b,獲得的是
5
函數(shù)內(nèi)部代碼執(zhí)行完畢,開始獲取變量b的值
雖然函數(shù)內(nèi)部也有b變量,但那是屬于函數(shù)自己的,并且這個變量在函數(shù)執(zhí)行完后被銷毀了,所以我們只能獲取到全局作用域下的變量b,內(nèi)容是
2
所以最終結(jié)果是:2、undefined、4、5、2
。
全局變量
全局變量除了在最外層聲明,其實還有另外一種聲明方式,不過這種方式并不受推薦,所以了解就好:
var a = 1; // 正常的聲明全局變量
function fun () {
b = 2; // 變量名前面沒有寫var,默認(rèn)成為全局作用域
};
// 因為函數(shù)還沒有執(zhí)行,所以這塊會報錯
console.log(b); // b not defined
fun();
console.log(b); // 2
三、let聲明
3.1 塊級作用域
相比于var變量,let顯得更加精致,因為它有自己的聲明準(zhǔn)則,其中最大的區(qū)別就是let聲明的范圍是塊級作用域,那么什么是塊級作用域呢,其實很好理解:{}
符號中的區(qū)域被稱為塊級作用域,需要注意的是這個區(qū)域?qū)ar并沒有影響。
塊級作用域和函數(shù)作用域相似,控制著內(nèi)部變量的讀取權(quán)限:
{ // 塊級作用域
let a1 = 2;
}
console.log(a1); // 報錯:a1 is not defined
3.2 暫時性死區(qū)
在let創(chuàng)建之前的瞬間被稱為暫時性死區(qū)
,聽起來很高級,其實就是在暫時性死區(qū)階段訪問不到let變量的意思,因為let變量沒有變量提升這一說;let還有一個特點就是不能被重復(fù)聲明,也就是說let變量不會像var變量一樣可以聲明多個重名變量,在let變量這會報錯的。
{
console.log(b); // 因為沒有變量提升,所以在聲明前訪問不到該變量,報錯:annot access 'b' before initialization
let b = 1;
}
let a = 1;
let a = 2; // 重名變量會報錯:caught SyntaxError: Identifier 'a' has already been declared
let的全局作用域
雖然前面說let聲明的范圍是塊級作用域,但如果在最外層全局作用域里聲明let變量,也會被認(rèn)為是合法的全局變量,與var變量不同的是let全局作用域不能通過window.變量名
訪問。
for循環(huán)中的let和var
for循環(huán)過程中會不斷產(chǎn)生新的塊級作用域,這個時候var和let變量值迭代更新就有了不同的變化:
let arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {};
console.log(i); // 報錯:i is not defined
for (var i = 0; i < arr.length; i++) {};
console.log(i); // 5
上面代碼中第一個循環(huán)內(nèi),使用let創(chuàng)建了一個變量,當(dāng)我們在循環(huán)體外訪問該變量時竟然報錯了!這是因為循環(huán)體使用了{}
,所以形成了塊級作用域,此時在塊作用域外訪問let變量肯定會報錯。當(dāng)我們訪問var變量的時候為什么沒有報錯呢?那是因為var變量不受塊級作用域影響,它成為了全局作用域。
四、const聲明
const聲明被稱為常量,為了更好理解,下文還是用變量進行介紹
const和let非常相似:不能聲明重名變量、存在暫時性死區(qū)、沒有變量提升、有塊級作用域限制
。這些都是非常nice的規(guī)則,在此基礎(chǔ)上,const還增加了兩個規(guī)則:1、聲明時必須賦值;2、值不可修改;
聲明時必須賦值這個很好理解,就是在創(chuàng)建的時候必須給這個變量一個初始值。我們詳細說一下值不可修改
這一點:
const a = '2';
a = 2; // 報錯a變量已經(jīng)聲明,不能重復(fù)創(chuàng)建:entifier 'a' has already been declared
上面代碼首先我們創(chuàng)建了const變量并為其賦值字符串類型的'2',在第二行代碼實際上是想把數(shù)字類型的2賦值給剛剛創(chuàng)建的const變量中,這時候確保錯了,由此可見const變量適合用在值不會變的場景。還有一種情況就是給const變量賦值數(shù)組和對象類型的值,因為這兩個數(shù)據(jù)類型屬于引用類型,所以只要在引用地址值不變的情況下,更改、新增、刪除對象或數(shù)組中的元素是不會報錯的。
總結(jié)
根據(jù)本篇文章的介紹,我們可以得出一個結(jié)論:盡量不用var,變量值需要變化的時候使用let,值不需要改變的時候使用const。
var | let | const | |
---|---|---|---|
變量提升 | √ | × | × |
重復(fù)聲明相同名稱變量 | √ | × | × |
是否可以更改變量值 | √ | √ | × |
函數(shù)作用域 | √ | × | × |
塊作用域 | × | √ | √ |