亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文讀懂JS中的var/let/const和暫時性死區(qū)

 更新時間:2023年02月27日 14:32:22   作者:趙晟鼎  
這篇文章主要為大家詳細(xì)介紹了JavaScript中的var、let、const和暫時性死區(qū)的異同,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下

js中變量的特征

js的變量是松散類型的。變量可以用于保存任何類型的數(shù)據(jù)。所以js也被稱為弱類型語言。

變量的定義與訪問

簡單說下作用域

什么是作用域,簡單來說就是這個變量起作用,能被訪問到、使用到的區(qū)域。

語法

定義

變量的值的存儲位置

變量聲明時從棧中申請空間來存儲變量的值。

使用關(guān)鍵字

可以使用var、let、const三個關(guān)鍵字來定義變量,后兩個關(guān)鍵字是自ES6才擁有的,推薦只使用后兩個,不用第一個。當(dāng)然考慮兼容性的話另說。

定義變量的關(guān)鍵詞 變量的標(biāo)識符名稱[[ = 初始值], 第二個變量名稱[ = 初始值2], 變量3[ = 初始值3], ...];

其中中括號括住的內(nèi)容為選寫內(nèi)容,...表示重復(fù)語法。

使用逗號間隔可以同時聲明多個變量。

變量 = 初始值相當(dāng)于給變量賦予一個初始的值。當(dāng)?shù)谝淮卧L問該變量時,若先前沒有對變量進(jìn)行改變的話,那么獲取到的就是這個初始值。這樣的操作我們稱為初始化。

若是省略初始化這一步,那么獲取到的值就是undefined,該值表示該變量沒有進(jìn)行初始化,值未定義。

例如

let a = 3, b, c = 4;這就生成了3個變量a,b,c,初始值分別為3,undefined,4。

不用關(guān)鍵字

直接給一個之前沒定義成變量的或是定義后失效的標(biāo)識符賦予一個值。那么在執(zhí)行完該賦值語句時,該標(biāo)識符將代表一個全局變量【可以先知道這么叫,具體理解需要作用域知識】。

從此刻位置開始,向下任何位置都可以訪問到該變量,哪怕你變量定義在一個塊中,我也可以在塊外訪問到。

但是若是在賦值語句前對該變量進(jìn)行訪問,會報錯。

此時該變量的作用域稱為全局作用域

console.log(m);//訪問不到,報錯導(dǎo)致整個程序終止,下面的語句不執(zhí)行
m = 109;
console.log(m);//刪除上面報錯的語句刪除后,該句會執(zhí)行,且訪問到

注意

當(dāng)這個語法放在函數(shù)中時,仍會創(chuàng)建出一個全局變量而不是局部變量。只不過當(dāng)這個函數(shù)在真正執(zhí)行之前,該變量都不會被創(chuàng)建。當(dāng)函數(shù)執(zhí)行之后,才會被創(chuàng)建。后面的代碼才能訪問到該變量。

這是因為js為解釋型語言??梢越瓶醋鹘馕鲆痪浯a執(zhí)行一句,當(dāng)然這么說不準(zhǔn)確,但可以簡單這樣子理解。所以函數(shù)未被執(zhí)行前,該全局變量無法被瀏覽器獲知,也就沒有創(chuàng)建。

function test() {
    m = 10;
}
//此行以及以上的代碼,除了test函數(shù)內(nèi)部外,都無法訪問m,m未被創(chuàng)建
test();//此行代碼執(zhí)行完畢后,可以訪問m

var

使用該關(guān)鍵字聲明的變量擁有的特點(diǎn):

聲明的變量為局部或是全局變量:

若是在一個函數(shù)中或是在一個塊中定義該變量,那么這個變量將會是屬于該塊【或函數(shù)】的局部變量。在塊或函數(shù)的內(nèi)部是可以訪問到的。但是在外部就不行了。同時也意味著,在出了這個塊的時候,該變量就會被銷毀。下次在訪問到該塊的時候,會創(chuàng)建一個同名的新的變量。而不是原來的。 不過定義在最外面的全局作用域中,它就變成了個全局變量。

function a() {
    var x = 1;
    console.log(x);//當(dāng)執(zhí)行函數(shù)時,該語句會輸出x
}
a();//輸出x的值,也就是1.說明x在a函數(shù)內(nèi)部可以訪問
console.log(x);//報錯

作用域提升:

我們知道當(dāng)我們訪問一個未定義的變量時,他會報錯。但是下方代碼卻好像違背了常識。

console.log(a);//不報錯且輸出undefined
var a = 1;
console.log(a);//1

按理說,解釋一句代碼,執(zhí)行一句代碼的話,只能在定義了變量之后,我才知道這個變量。那為什么使用了var定義后就可以在前面訪問呢?

這是因為js會將使用var所定義的變量的作用域提升,將var a = 1;分成了兩個部分:var a;a = 1;。并且將var a;放到a定義位置所處的那個塊中的最前面【最外層的代碼我們說他處于一個同步代碼塊中,雖然沒有花括號,但是也看做一個塊】。并將a = 1;留在了原地。

所以上面的代碼可以認(rèn)為等同于下方代碼。

注意:變量提升【作用域提升】,僅僅會將變量的聲明提升,初始化的賦值語句則會留在原地。所以塊開頭到聲明部分的中間那段區(qū)域中,變量的值為undefined

var a;
console.log(a);//不報錯且輸出undefined
a = 1;
console.log(a);//1

同樣下方兩個代碼效果相同:

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

定義的變量的作用域為函數(shù)作用域或全局作用域

當(dāng)var定義的變量是函數(shù)作用域時,var是在塊中定義的變量。從塊的開始到塊的結(jié)束都能訪問到他所定義的變量。

當(dāng)var定義的變量為全局作用域時,var是在最外層的同步代碼塊定義的變量。在代碼中的任何一個位置都可訪問到。

聲明的變量若是在最外層同步代碼塊【也叫全局作用域】中會作為window對象的屬性

字面意思。當(dāng)定義變量語句放在全局作用域中,那么它就會被掛載在全局上下文的變量對象身上【這里以后講上下文就懂了】,而全局上下文的變量對象可以通過window訪問。

所以會作為window的屬性。

允許冗余聲明

即允許var定義多個同名的變量。

你可以將它理解為:var定義的語句都被分為var 標(biāo)識符;標(biāo)識符 = 初始值;兩部分。第一部分都被提到塊的最前面,重合的都被合并。第二部分留在原位。

所以相當(dāng)于一個變量在不同的位置被不停地變換值而已。

var a = 1, a = 2;
console.log("結(jié)果");
console.log(a);//輸出2
var a = 3;
var a;
a = 1;
var a;
console.log(a)//輸出1

for循環(huán)頭部定義的變量不會滲透在循環(huán)外部

比如說:

for(var i = 0; i < 5; i++) { 
    console.log(i); 
}
console.log(i);

在輸出1,2,3,4,5后,仍然會輸出5,而不是報錯。

其實就是相當(dāng)于下面的代碼:

var i;
for(i = 0; i < 5; i++) { 
    console.log(i); 
}
console.log(i);

let與暫時性死區(qū)

使用let命名的變量所擁有的特點(diǎn)與var差不多。但是【下面就是5個區(qū)別了,唯2的共同點(diǎn)可以說是定義的語法和定義出來的變量可以為局部或是全局變量吧】

不具作用域提升這個特點(diǎn)

而從塊的開頭到定義這個變量的位置之間的區(qū)域,我們叫做暫時性死區(qū)。

let定義的變量為塊級作用域

根據(jù)作用域的概念來講。由于var與let所定義的變量的可訪問的區(qū)域不同。

所以let定義的變量的作用域自然與var所定義的作用域不同。

塊級作用域:變量從定義處到塊結(jié)束都可以被訪問到,其他地方不行。

不允許同一個塊中冗余聲明

同字面意思。

首先,不允許let在同一個塊中定義多個標(biāo)識符相同的變量。

而且,若是有不用標(biāo)識符定義的全局變量或是var定義的變量,與let定義的變量的標(biāo)識符相同,且處于同一個塊中,那么,會報錯。

但是嵌套重復(fù)定義是允許的?!緅s中聲明和定義是一回事】【以下理論出自紅寶書】

由于js引擎【就是負(fù)責(zé)執(zhí)行js代碼的那個東東】會記錄:聲明或使用的標(biāo)識符和該標(biāo)識符所在的塊作用域。

并且進(jìn)行對比查重。而查重過程中只有塊作用域不同而標(biāo)識符相同時,才會報冗余定義的錯。

像是let a = 1; a = 3;這段代碼中,這兩個a是同一個塊作用域。

let a = 2,a = 3;這段代碼中,這兩個a是不同的塊作用域,因為聲明的位置不同,所以該變量的塊作用域的起始位置不同,塊作用域本身自然不同。所以會報錯。

所以若是在不同的塊中定義相同的變量是可以的,不會報錯。

但是多層嵌套重復(fù)定義的變量在使用時究竟用哪一個呢

這種現(xiàn)象我們叫做作用域覆蓋。其實聽名字都能大概猜出最終會用哪個了。

外層的作用域被內(nèi)層的作用域覆蓋掉。而當(dāng)前重復(fù)的區(qū)域的歸屬權(quán)自然不是被覆蓋掉的作用域,而是覆蓋者。而變量使用誰自然是看當(dāng)前位置是處于哪個作用域中。

function a() {
    let a = 1;
    //下方對a訪問是訪問a變量而不是a函數(shù),也算是一個作用域覆蓋。
    console.log(a);//輸出1
    function b() {//若是改成a也會報錯。同樣冗余定義
        //console.log(a); 會報錯,也是冗余問題。使用的也會被記錄
        let a = "s";
        console.log(a);
    }
    b();//輸出s
}
a();//執(zhí)行的是上面的函數(shù)。

在全局作用域中定義變量,變量不會作為window的屬性

字面意思,let定義的變量他不會被掛載在全局上下文的變量對象上。但是它仍然是在全局作用域中被定義的,所以在全局的定義位置到全局結(jié)束都可訪問到該變量。

for循環(huán)頭部定義的變量會滲透到循環(huán)外部

與var不同,let在for頭部定義的變量并不會滲透出去。它相當(dāng)于定義在了for循環(huán)的循環(huán)體的那個代碼塊內(nèi)部。

for (let i = 0; i < 5; i++) {
    console.log(i);
}
console.log(i);//報錯
//和下方的代碼效果等價
while(true) {
    let i;
    if (i < 5) {
        console.log(i);
    }
    else
        break;
    i++;
}
console.log(i);

像這種的區(qū)別要是單純使用for循環(huán)那么不足一提。但是若是在for循環(huán)使用閉包函數(shù)引用這個迭代變量i,就會出問題了。

最典型的例子是這個:【不懂閉包的就光看結(jié)果吧,以后介紹閉包的時候會舊事重提的?!?/p>

for(var i = 0; i < 5; i++) {
    setTimeout(()=>console.log(i), 4);
}
//最終輸出5個5,等同于下面代碼
var i;
for(i = 0; i < 5; i++) { 
    setTimeout(()=>console.log(i), 4);
}

setTimeout是延時函數(shù),第一個參數(shù)是回調(diào)函數(shù)【要執(zhí)行的函數(shù)】,第二個參數(shù)是延遲的時間【單位ms】。表示延遲多長時間執(zhí)行該函數(shù)。

由于閉包函數(shù)會延長變量的生命周期。所以i的生命周期會被延長。當(dāng)執(zhí)行setTimeout函數(shù)的回調(diào)【就是第一個參數(shù)】時,i仍然是當(dāng)時調(diào)用setTimeout函數(shù)時的那層for循環(huán)的i。但是由于每一層for循環(huán)使用的其實是同一個變量。而回調(diào)又是延時后執(zhí)行的,for循環(huán)在回調(diào)執(zhí)行時早就運(yùn)行完了。所以最終輸出的會是i這個唯一的迭代變量在經(jīng)過5次循環(huán)后的值——5.

而若是let則沒這個問題了。

for (let i = 0; i < 5; i++) {
    setTimeout(()=>console.log(i), 4);
}
//和下方的代碼效果等價
while(true) {
    let i;
    if (i < 5) {
        setTimeout(()=>console.log(i), 4);
    }
    else
        break;
    i++;
}

由于每一層循環(huán)都是一個新的變量。所以回調(diào)所引用的是不同的變量。輸出的自然是我們期望的0,1,2,3,4

const

和let的效果、特點(diǎn)近乎一樣。

唯一的不同就是:const變量在定義的同時必須初始化。再者定義完成后變量對應(yīng)的那個棧中的存儲空間的值就不允許被改變了,否則會報錯?!咀兞柯暶鲿r從棧中申請空間來存儲變量的值】

透露一點(diǎn)。若是const變量被賦值為一個js對象【準(zhǔn)確的來說是一個引用類型的數(shù)據(jù)】時,對象的屬性是允許被改變的。因為const僅僅限制了棧中值不允許被改變。而對象在給變量時,是將這個對象本身的地址賦給了棧中的存儲空間。而他自身的數(shù)據(jù)存儲在堆之中。所以堆中的數(shù)據(jù)如何修改不關(guān)const的事。

不同<script>中聲明的變量能否使用

可以使用,無論是內(nèi)嵌代碼塊還是外部引用的代碼塊,但凡是該頁面中的代碼。只要是已經(jīng)聲明且聲明在全局作用域中,那么在定義的<script>后面的<script>中就可以訪問到該變量。

但是有一個要注意的點(diǎn)是:一定要保證一個標(biāo)識符在之前的代碼塊中沒有被定義過,才能再次定義,否則會報錯。

COOKBOOK

  • 推薦使用關(guān)鍵字來創(chuàng)建變量。因為在塊中定義的全局變量很難維護(hù),容易造成困惑。且在嚴(yán)格模式下不允許不使用關(guān)鍵字就創(chuàng)建變量。
  • 當(dāng)要考慮兼容不允許ES6的瀏覽器時,請全部使用var定義變量。
  • 當(dāng)不用考慮ES6以前標(biāo)準(zhǔn)的兼容時,請盡量全部使用const和var。其中不改變的一些內(nèi)容,要使用const定義。最好做到const優(yōu)先使用。

到此這篇關(guān)于一文讀懂JS中的var/let/const和暫時性死區(qū)的文章就介紹到這了,更多相關(guān)JS var let const內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論