欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

js中變量的特征

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

變量的定義與訪問(wèn)

簡(jiǎn)單說(shuō)下作用域

什么是作用域,簡(jiǎn)單來(lái)說(shuō)就是這個(gè)變量起作用,能被訪問(wèn)到、使用到的區(qū)域。

語(yǔ)法

定義

變量的值的存儲(chǔ)位置

變量聲明時(shí)從棧中申請(qǐng)空間來(lái)存儲(chǔ)變量的值。

使用關(guān)鍵字

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

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

其中中括號(hào)括住的內(nèi)容為選寫(xiě)內(nèi)容,...表示重復(fù)語(yǔ)法。

使用逗號(hào)間隔可以同時(shí)聲明多個(gè)變量。

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

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

例如

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

不用關(guān)鍵字

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

從此刻位置開(kāi)始,向下任何位置都可以訪問(wèn)到該變量,哪怕你變量定義在一個(gè)塊中,我也可以在塊外訪問(wèn)到。

但是若是在賦值語(yǔ)句前對(duì)該變量進(jìn)行訪問(wèn),會(huì)報(bào)錯(cuò)。

此時(shí)該變量的作用域稱為全局作用域

console.log(m);//訪問(wèn)不到,報(bào)錯(cuò)導(dǎo)致整個(gè)程序終止,下面的語(yǔ)句不執(zhí)行
m = 109;
console.log(m);//刪除上面報(bào)錯(cuò)的語(yǔ)句刪除后,該句會(huì)執(zhí)行,且訪問(wèn)到

注意

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

這是因?yàn)閖s為解釋型語(yǔ)言。可以近似看作解析一句代碼執(zhí)行一句,當(dāng)然這么說(shuō)不準(zhǔn)確,但可以簡(jiǎn)單這樣子理解。所以函數(shù)未被執(zhí)行前,該全局變量無(wú)法被瀏覽器獲知,也就沒(méi)有創(chuàng)建。

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

var

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

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

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

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

作用域提升:

我們知道當(dāng)我們?cè)L問(wèn)一個(gè)未定義的變量時(shí),他會(huì)報(bào)錯(cuò)。但是下方代碼卻好像違背了常識(shí)。

console.log(a);//不報(bào)錯(cuò)且輸出undefined
var a = 1;
console.log(a);//1

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

這是因?yàn)閖s會(huì)將使用var所定義的變量的作用域提升,將var a = 1;分成了兩個(gè)部分:var a;a = 1;。并且將var a;放到a定義位置所處的那個(gè)塊中的最前面【最外層的代碼我們說(shuō)他處于一個(gè)同步代碼塊中,雖然沒(méi)有花括號(hào),但是也看做一個(gè)塊】。并將a = 1;留在了原地。

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

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

var a;
console.log(a);//不報(bào)錯(cuò)且輸出undefined
a = 1;
console.log(a);//1

同樣下方兩個(gè)代碼效果相同:

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

定義的變量的作用域?yàn)楹瘮?shù)作用域或全局作用域

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

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

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

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

所以會(huì)作為window的屬性。

允許冗余聲明

即允許var定義多個(gè)同名的變量。

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

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

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ì)滲透在循環(huán)外部

比如說(shuō):

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

在輸出1,2,3,4,5后,仍然會(huì)輸出5,而不是報(bào)錯(cuò)。

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

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

let與暫時(shí)性死區(qū)

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

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

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

let定義的變量為塊級(jí)作用域

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

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

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

不允許同一個(gè)塊中冗余聲明

同字面意思。

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

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

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

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

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

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

let a = 2,a = 3;這段代碼中,這兩個(gè)a是不同的塊作用域,因?yàn)槁暶鞯奈恢貌煌?,所以該變量的塊作用域的起始位置不同,塊作用域本身自然不同。所以會(huì)報(bào)錯(cuò)。

所以若是在不同的塊中定義相同的變量是可以的,不會(huì)報(bào)錯(cuò)。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

而若是let則沒(méi)這個(gè)問(wèn)題了。

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

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

const

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

唯一的不同就是:const變量在定義的同時(shí)必須初始化。再者定義完成后變量對(duì)應(yīng)的那個(gè)棧中的存儲(chǔ)空間的值就不允許被改變了,否則會(huì)報(bào)錯(cuò)?!咀兞柯暶鲿r(shí)從棧中申請(qǐng)空間來(lái)存儲(chǔ)變量的值】

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

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

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

但是有一個(gè)要注意的點(diǎn)是:一定要保證一個(gè)標(biāo)識(shí)符在之前的代碼塊中沒(méi)有被定義過(guò),才能再次定義,否則會(huì)報(bào)錯(cuò)。

COOKBOOK

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

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

相關(guān)文章

最新評(píng)論