作用域
在 JavaScript 中有兩種作用域
- 全局作用域
- 局部作用域
當(dāng)變量定義在一個函數(shù)中時,變量就在局部作用域中陕贮,而定義在函數(shù)之外的變量則從屬于全局作用域。每個函數(shù)在調(diào)用的時候會創(chuàng)建一個新的作用域昌执。
全局作用域
最外層函數(shù)定義的變量擁有全局作用域萄窜,全局作用域里的變量能夠在其他作用域中被訪問和修改。
var name = "Tennant";
console.log(name);//Tennant
function logName(){
console.log(name);
}
logName();//Tennant
局部作用域
和全局作用域相反循榆,局部作用域一般只在固定的代碼片段內(nèi)可訪問到析恢,而對于函數(shù)外部是無法訪問的,最常見的例如函數(shù)內(nèi)部
function fn(){
var name = "Tennant";
}
fn();
console.log(name);//undefined
需要注意的是秧饮,函數(shù)內(nèi)部聲明變量的時候映挂,一定要使用var命令。如果不用的話盗尸,實際上聲明了一個全局變量:
function fn(){
name = "Tennant";
}
fn();
console.log(name);//Tennant
再如以下代碼柑船,只要函數(shù)內(nèi)定義了一個局部變量,函數(shù)在解析的時候都會將這個變量“提前聲明” :
var scope = "global";
function fn(){
console.log(scope);//undefined
var scope = "local";
console.log(scope);//local
}
fn();
var scope = "global";
function fn(){
var scope;//提前聲明了局部變量
console.log(scope);//undefined
scope = "local";
console.log(scope);//local
}
fn();
沒有塊級作用域
Javascript沒有塊級作用域泼各,在其他類C的語言中鞍时,由花括號封閉的代碼塊都有自己的作用域,因此支持條件來定義變量。例如逆巍,以下代并不會得到想要的結(jié)果:
if(true){
var name = "Tennant";
}
console.log(name);//Tennant
這里是在一個if語句中定義了變量name及塘。如果是在C、C++或者Java中蒸苇,name會在if語句執(zhí)行完畢后被銷毀。但在JavaScript中吮旅,if語句中的變量聲明會將變量添加到當(dāng)前的執(zhí)行環(huán)境(在這里是全局環(huán)境)中溪烤。在使用for語句時要特別注意,例如:
for (var i = 0; i < 10; i++){
doSomething(i);
}
console.log(i);//10
對于有塊級作用域的語言來說庇勃,for語句初始化變量的表達式所定義的變量檬嘀,只會存在于循環(huán)的環(huán)境之中。而對于JavaScript來說责嚷,由for語句創(chuàng)建的變量i即使在for循環(huán)執(zhí)行結(jié)束后鸳兽,也依舊會存在于循環(huán)外部的執(zhí)行環(huán)境中。
作用域鏈(Scope chain)
當(dāng)代碼在一個環(huán)境執(zhí)行時罕拂,會創(chuàng)建變量對象的一個作用域鏈揍异。作用域鏈的作用是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。
執(zhí)行環(huán)境
執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)爆班,決定了它們各自的行為衷掷。每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中戚嗅。
全局執(zhí)行環(huán)境是最外圍的執(zhí)行環(huán)境枢舶,全局執(zhí)行環(huán)境被認(rèn)為是window對象凉泄,因此所有的全局變量和函數(shù)都作為window對象的屬性和方法創(chuàng)建的。
JavaScript的執(zhí)行順序是根據(jù)函數(shù)的調(diào)用來決定的醇份,當(dāng)一個函數(shù)被調(diào)用時吼具,該函數(shù)環(huán)境的變量對象就被壓入一個環(huán)境棧中拗盒。而在函數(shù)執(zhí)行之后,棧將該函數(shù)的變量對象彈出痊臭,把控制權(quán)交給之前的執(zhí)行環(huán)境變量對象广匙。
舉個例子:
var color = "blue";
function changeColor(){
if(color === "blue"){
color = "red";
}else{
color = "blue";
}
}
changeColor();
alert(color);//red
在以上例子中,函數(shù)changeColor()的作用域鏈包含兩個對象:
它自己的變量對象(其中定義著arguments對象)和全局環(huán)境的變量對象潮剪》滞伲可以在函數(shù)內(nèi)部訪問變量color,就是因為可以在這個作用域鏈中找到它弧蝇。
看如下代碼:
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn() //輸出多少
//輸出a=2
//執(zhí)行fn2函數(shù)折砸,fn2找不到變量a,接著往上在找到創(chuàng)建當(dāng)前fn2所在的作用域fn1中找到a=2
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
var a
fn2()
a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn() //輸出多少
//輸出undefined
//函數(shù)fn2在執(zhí)行的過程中,先從自己內(nèi)部找變量找不到鹃觉,再從創(chuàng)建當(dāng)前函數(shù)所在的作用域fn去找,注意此時變量聲明前置睹逃,a已聲明但未初始化為undefined
以上查找方向為:
- 函數(shù)在執(zhí)行的過程中,先從自己內(nèi)部找變量(注意找的是變量的當(dāng)前的狀態(tài))
- 如果找不到沉填,再從創(chuàng)建當(dāng)前函數(shù)所在的作用域去找, 以此往上