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