JavaScript作用域與作用域鏈?zhǔn)褂弥攸c(diǎn)講解
作用域和作用域鏈方面的知識是JS的重點(diǎn),去面試十個有八個都會問你這塊的知識,所以說這塊是特別特別的重要,下面我們好好理解一下作用域和作用域鏈到底是個什么:
先上一段代碼:
var a = 'jack'; function fn() { var a = 'frank'; } console.log(a);
我們在函數(shù)里定義了一個a變量,在函數(shù)外也定義了一個a變量,那最后輸出的應(yīng)該是哪一個a的值呢?
這個時候就有了作用域這個概念了,簡單地說作用域就是限制某個變量只能在某個區(qū)域內(nèi)有效。
作用域有全局作用域和局部作用域之分,變量同樣如此,在上例中,第一個a很顯然是一個全局變量,函數(shù)內(nèi)的a顯然是局部變量。全局變量擁有全局作用域而局部變量擁有局部作用域。這道題里console.log是在全局里調(diào)用a,那么毋庸置疑最后輸出的一定是'jack'。
這個時候我把函數(shù)代碼塊改為if代碼塊,看看最后應(yīng)該輸出什么呢
var a = 'jack'; if(true) { var a = 'frank'; } console.log(a);
最后的結(jié)果a輸出的是'frank'。
實(shí)際上這里有一個大坑,千萬不要以為大括號封起來就一定是封閉環(huán)境,if里面的語句執(zhí)行完后就會自動銷毀了,但是在javascript里if內(nèi)部定義的變量就會變?yōu)楫?dāng)前執(zhí)行環(huán)境的變量。當(dāng)前執(zhí)行環(huán)境在最外圍,所以if里面的a就變?yōu)槿肿兞苛?/p>
我們再來看下面這段代碼分別應(yīng)該輸出什么呢?
for(var i = 0;i<3;i++) { break; } console.log(i); k = 5; while(k>1) { k--; var d = 10; } console.log(k); console.log(d);
除了if代碼塊還有我們常見的for循環(huán),while循環(huán)也是相似的結(jié)果,我們不要被括號給迷惑了,在括號內(nèi)定義的變量不一定就是局部作用域,因此這里的i,k,d變量都是全局變量,這是輸出結(jié)果:
下面結(jié)合es6新增的塊級作用域做一個總的概括:
- 在ES6中只要{ }沒有和函數(shù)結(jié)合在一起,那么應(yīng)該就是“塊級作用域”。
- 在塊級作用域中,var定義的變量是全局變量,let定義的變量是局部變量。
- 而在局部作用域也就是函數(shù)作用域中,無論是用var定義的變量還是用let定義的變量都是局部變量。
- 無論是在塊級作用域還是局部作用域,省略變量前面的var或者let都會變成一個全局變量。
現(xiàn)在我們再回到前面的例子,這一次增加了全局變量b,在函數(shù)內(nèi)增加了兩個console.log輸出語句,最后再調(diào)用這個函數(shù),但是在函數(shù)里并沒有定義變量b,那最后會是什么結(jié)果呢
var a = 'jack'; var b = 'andy'; function fn() { var a = 'frank'; console.log(a); console.log(b); } fn(); console.log(a);
輸出結(jié)果:
第二個console.log為什么會輸出全局變量andy呢?
這個時候就有了作用域鏈的概念了,簡單的說作用域表示區(qū)域,作用域鏈表示次序
現(xiàn)在我們把眼光放在函數(shù)fn里,第一行定義了a是局部變量,第二行輸出這個a,但是整個代碼里定義了兩個a,那么就需要剛剛說到的作用域鏈來決定到底先用哪個變量。
javascript會先看函數(shù)內(nèi)有沒有這個變量a,如果沒有再去函數(shù)的外圍看有沒有這個變量,這里作用域鏈就幫我們安排好了這個次序。
所以,函數(shù)內(nèi)定義了變量a為'frank',那么第二行就會輸出'frank',第三行要輸出變量b,我們先看函數(shù)內(nèi)有沒有這個變量,發(fā)現(xiàn)沒有,再去外圍發(fā)現(xiàn)有全局變量b,那么輸出的就是這個值,我們再來看最后一個console.log(a),因為他在全局范圍內(nèi),所以只能訪問全局變量a。
也就是說:作用域鏈只能向上查找,最終找到全局。不能同級(局部)或者向下查找
我們再看這一段代碼:
var a = 'jack'; function fn() { console.log(a); var a = 'andy'; console.log(a); } fn();
我們思考一下會輸出什么呢?
輸出結(jié)果:
稍微有點(diǎn)js經(jīng)驗的同學(xué)應(yīng)該都會答對,因為有變量提升,變量a在第一行就被聲明了,只不過沒有被賦值。下面修改一下代碼,大家再看看會輸出什么:
var a = 'jack'; function fn() { console.log(a); var a = 'andy'; console.log(ss()); function ss() { return a; } } fn();
我們在fn函數(shù)內(nèi)又添加了一個函數(shù)ss,并且在這個函數(shù)的頂部就調(diào)用了這個函數(shù)。不僅函數(shù)內(nèi)聲明的變量會被提升,函數(shù)內(nèi)的函數(shù)也會被提升,而且函數(shù)的提升會比變量更優(yōu)先
那么,在javascript中這段代碼實(shí)際上是這樣被執(zhí)行的:
var a = 'jack'; function fn() { function ss() { return a; } var a; console.log(a); a = 'andy'; console.log(ss()); } fn();
先把函數(shù)聲明提升到首行,再聲明變量a,然后輸出a,a沒有被賦值,所以是undefined,然后a被賦值為'andy',最后調(diào)用函數(shù)ss,返回的就是andy。
到此這篇關(guān)于JavaScript作用域與作用域鏈?zhǔn)褂弥攸c(diǎn)講解的文章就介紹到這了,更多相關(guān)JS作用域與作用域鏈內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript showModalDialog 內(nèi)跳轉(zhuǎn)頁面的問題
在頁面中使用了showModalDialog,但是在跳轉(zhuǎn)鏈接時,不會在當(dāng)前頁執(zhí)行,而是彈出一個新的頁面。2010-11-11javascript中的startWith和endWith的幾種實(shí)現(xiàn)方法
javascript中的startWith和endWith的幾種實(shí)現(xiàn)方法,需要的朋友可以參考一下2013-05-05IE6/7中g(shù)etAttribute獲取href/src 屬性(相對路徑0值與其它瀏覽器不同
IE6/7中g(shù)etAttribute獲取href/src 屬性(相對路徑0值與其它瀏覽器不同的解決方法2011-08-08在服務(wù)端(Page.Write)調(diào)用自定義的JS方法詳解
自從[javascript]自定義MessageBox一文發(fā)布以后,很多網(wǎng)友都來信詢問,如何在服務(wù)端調(diào)用ShowInfo方法,周末休息想了個折中的辦法來實(shí)現(xiàn)2013-08-08