JavaScript作用域與作用域鏈使用重點講解
作用域和作用域鏈方面的知識是JS的重點,去面試十個有八個都會問你這塊的知識,所以說這塊是特別特別的重要,下面我們好好理解一下作用域和作用域鏈到底是個什么:
先上一段代碼:
var a = 'jack';
function fn() {
var a = 'frank';
}
console.log(a);我們在函數(shù)里定義了一個a變量,在函數(shù)外也定義了一個a變量,那最后輸出的應該是哪一個a的值呢?
這個時候就有了作用域這個概念了,簡單地說作用域就是限制某個變量只能在某個區(qū)域內有效。
作用域有全局作用域和局部作用域之分,變量同樣如此,在上例中,第一個a很顯然是一個全局變量,函數(shù)內的a顯然是局部變量。全局變量擁有全局作用域而局部變量擁有局部作用域。這道題里console.log是在全局里調用a,那么毋庸置疑最后輸出的一定是'jack'。
這個時候我把函數(shù)代碼塊改為if代碼塊,看看最后應該輸出什么呢
var a = 'jack';
if(true) {
var a = 'frank';
}
console.log(a);最后的結果a輸出的是'frank'。
實際上這里有一個大坑,千萬不要以為大括號封起來就一定是封閉環(huán)境,if里面的語句執(zhí)行完后就會自動銷毀了,但是在javascript里if內部定義的變量就會變?yōu)楫斍皥?zhí)行環(huán)境的變量。當前執(zhí)行環(huán)境在最外圍,所以if里面的a就變?yōu)槿肿兞苛?/p>
我們再來看下面這段代碼分別應該輸出什么呢?
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)也是相似的結果,我們不要被括號給迷惑了,在括號內定義的變量不一定就是局部作用域,因此這里的i,k,d變量都是全局變量,這是輸出結果:

下面結合es6新增的塊級作用域做一個總的概括:
- 在ES6中只要{ }沒有和函數(shù)結合在一起,那么應該就是“塊級作用域”。
- 在塊級作用域中,var定義的變量是全局變量,let定義的變量是局部變量。
- 而在局部作用域也就是函數(shù)作用域中,無論是用var定義的變量還是用let定義的變量都是局部變量。
- 無論是在塊級作用域還是局部作用域,省略變量前面的var或者let都會變成一個全局變量。
現(xiàn)在我們再回到前面的例子,這一次增加了全局變量b,在函數(shù)內增加了兩個console.log輸出語句,最后再調用這個函數(shù),但是在函數(shù)里并沒有定義變量b,那最后會是什么結果呢
var a = 'jack';
var b = 'andy';
function fn() {
var a = 'frank';
console.log(a);
console.log(b);
}
fn();
console.log(a);輸出結果:

第二個console.log為什么會輸出全局變量andy呢?
這個時候就有了作用域鏈的概念了,簡單的說作用域表示區(qū)域,作用域鏈表示次序
現(xiàn)在我們把眼光放在函數(shù)fn里,第一行定義了a是局部變量,第二行輸出這個a,但是整個代碼里定義了兩個a,那么就需要剛剛說到的作用域鏈來決定到底先用哪個變量。
javascript會先看函數(shù)內有沒有這個變量a,如果沒有再去函數(shù)的外圍看有沒有這個變量,這里作用域鏈就幫我們安排好了這個次序。
所以,函數(shù)內定義了變量a為'frank',那么第二行就會輸出'frank',第三行要輸出變量b,我們先看函數(shù)內有沒有這個變量,發(fā)現(xiàn)沒有,再去外圍發(fā)現(xiàn)有全局變量b,那么輸出的就是這個值,我們再來看最后一個console.log(a),因為他在全局范圍內,所以只能訪問全局變量a。
也就是說:作用域鏈只能向上查找,最終找到全局。不能同級(局部)或者向下查找
我們再看這一段代碼:
var a = 'jack';
function fn() {
console.log(a);
var a = 'andy';
console.log(a);
}
fn();我們思考一下會輸出什么呢?
輸出結果:

稍微有點js經驗的同學應該都會答對,因為有變量提升,變量a在第一行就被聲明了,只不過沒有被賦值。下面修改一下代碼,大家再看看會輸出什么:
var a = 'jack';
function fn() {
console.log(a);
var a = 'andy';
console.log(ss());
function ss() {
return a;
}
}
fn();我們在fn函數(shù)內又添加了一個函數(shù)ss,并且在這個函數(shù)的頂部就調用了這個函數(shù)。不僅函數(shù)內聲明的變量會被提升,函數(shù)內的函數(shù)也會被提升,而且函數(shù)的提升會比變量更優(yōu)先
那么,在javascript中這段代碼實際上是這樣被執(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',最后調用函數(shù)ss,返回的就是andy。
到此這篇關于JavaScript作用域與作用域鏈使用重點講解的文章就介紹到這了,更多相關JS作用域與作用域鏈內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

