作用域
在 JavaScript 中有兩種作用域
- 全局作用域
- 局部作用域
當(dāng)變量定義在一個(gè)函數(shù)中時(shí),變量就在局部作用域中,而定義在函數(shù)之外的變量則從屬于全局作用域。每個(gè)函數(shù)在調(diào)用的時(shí)候會(huì)創(chuàng)建一個(gè)新的作用域。
全局作用域
最外層函數(shù)定義的變量擁有全局作用域,全局作用域里的變量能夠在其他作用域中被訪問和修改。
var name = "Tennant";
console.log(name);//Tennant
function logName(){
console.log(name);
}
logName();//Tennant
局部作用域
和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,而對(duì)于函數(shù)外部是無法訪問的,最常見的例如函數(shù)內(nèi)部
function fn(){
var name = "Tennant";
}
fn();
console.log(name);//undefined
需要注意的是,函數(shù)內(nèi)部聲明變量的時(shí)候,一定要使用var命令。如果不用的話,實(shí)際上聲明了一個(gè)全局變量:
function fn(){
name = "Tennant";
}
fn();
console.log(name);//Tennant
再如以下代碼,只要函數(shù)內(nèi)定義了一個(gè)局部變量,函數(shù)在解析的時(shí)候都會(huì)將這個(gè)變量“提前聲明” :
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();
沒有塊級(jí)作用域
Javascript沒有塊級(jí)作用域,在其他類C的語言中,由花括號(hào)封閉的代碼塊都有自己的作用域,因此支持條件來定義變量。例如,以下代并不會(huì)得到想要的結(jié)果:
if(true){
var name = "Tennant";
}
console.log(name);//Tennant
這里是在一個(gè)if語句中定義了變量name。如果是在C、C++或者Java中,name會(huì)在if語句執(zhí)行完畢后被銷毀。但在JavaScript中,if語句中的變量聲明會(huì)將變量添加到當(dāng)前的執(zhí)行環(huán)境(在這里是全局環(huán)境)中。在使用for語句時(shí)要特別注意,例如:
for (var i = 0; i < 10; i++){
doSomething(i);
}
console.log(i);//10
對(duì)于有塊級(jí)作用域的語言來說,for語句初始化變量的表達(dá)式所定義的變量,只會(huì)存在于循環(huán)的環(huán)境之中。而對(duì)于JavaScript來說,由for語句創(chuàng)建的變量i即使在for循環(huán)執(zhí)行結(jié)束后,也依舊會(huì)存在于循環(huán)外部的執(zhí)行環(huán)境中。
作用域鏈(Scope chain)
當(dāng)代碼在一個(gè)環(huán)境執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈。作用域鏈的作用是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。
執(zhí)行環(huán)境
執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。
全局執(zhí)行環(huán)境是最外圍的執(zhí)行環(huán)境,全局執(zhí)行環(huán)境被認(rèn)為是window對(duì)象,因此所有的全局變量和函數(shù)都作為window對(duì)象的屬性和方法創(chuàng)建的。
JavaScript的執(zhí)行順序是根據(jù)函數(shù)的調(diào)用來決定的,當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),該函數(shù)環(huán)境的變量對(duì)象就被壓入一個(gè)環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將該函數(shù)的變量對(duì)象彈出,把控制權(quán)交給之前的執(zhí)行環(huán)境變量對(duì)象。
舉個(gè)例子:
var color = "blue";
function changeColor(){
if(color === "blue"){
color = "red";
}else{
color = "blue";
}
}
changeColor();
alert(color);//red
在以上例子中,函數(shù)changeColor()的作用域鏈包含兩個(gè)對(duì)象:
它自己的變量對(duì)象(其中定義著arguments對(duì)象)和全局環(huán)境的變量對(duì)象。可以在函數(shù)內(nèi)部訪問變量color,就是因?yàn)榭梢栽谶@個(gè)作用域鏈中找到它。
看如下代碼:
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去找,注意此時(shí)變量聲明前置,a已聲明但未初始化為undefined
以上查找方向?yàn)椋?/p>
- 函數(shù)在執(zhí)行的過程中,先從自己內(nèi)部找變量(注意找的是變量的當(dāng)前的狀態(tài))
- 如果找不到,再從創(chuàng)建當(dāng)前函數(shù)所在的作用域去找, 以此往上