作用域
變量作用域有兩種:全局變量和局部變量。
變量在函數(shù)外定義依疼,即為全局變量贡定,全局變量有全局作用域:網(wǎng)頁(yè)中所有腳本和函數(shù)均可使用晶伦。例如:
var arr = 'hello';
function fn() {
console.log(arr);
}
fn(); // hello
變量在函數(shù)內(nèi)申明碟狞,變量為局部作用域,局部變量:只能在函數(shù)內(nèi)部訪問(wèn)婚陪。例如:
function fn() {
var arr1 = 'world';
}
fn();
console.log(arr1); // arr1 is not defined
注:聲明局部變量時(shí)一定要使用var族沃,否則聲明的是全局變量。
因?yàn)榫植孔兞恐蛔饔糜诤瘮?shù)內(nèi)泌参,所以不同的函數(shù)可以使用相同名稱的變量竭业。局部變量在函數(shù)開始執(zhí)行時(shí)創(chuàng)建,函數(shù)執(zhí)行完后局部變量會(huì)自動(dòng)銷毀及舍。
在函數(shù)內(nèi)定義一個(gè)局部變量在函數(shù)體內(nèi)始終是可見的,函數(shù)在解析時(shí)會(huì)將變量聲明提前窟绷。例如:
var arr2 = 'hello';
function fn() {
console.log(arr2); // undefined
var arr2 = 'world';
console.log(arr2); // world锯玛,局部變量覆蓋了全局變量
}
fn();
var arr2 = 'hello';
function fn() {
var arr2; // 提前聲明了局部變量
console.log(arr2); // undefined
arr2 = 'world';
console.log(arr2); // world,局部變量覆蓋了全局變量
}
fn();
注:局部變量的優(yōu)先級(jí)要高于同名的全局變量兼蜈,也就是當(dāng)局部變量與全局變量重名時(shí)攘残,局部變量會(huì)覆蓋全局變量。
作用域鏈
執(zhí)行環(huán)境:
? ? ? ? 執(zhí)行環(huán)境是在運(yùn)行和執(zhí)行代碼的時(shí)候才存在的为狸,運(yùn)行瀏覽器的時(shí)候會(huì)創(chuàng)建全局的執(zhí)行環(huán)境歼郭,在調(diào)用函數(shù)時(shí),會(huì)創(chuàng)建函數(shù)執(zhí)行環(huán)境辐棒。所以執(zhí)行環(huán)境有全局執(zhí)行環(huán)境(全局環(huán)境)和函數(shù)執(zhí)行環(huán)境之分病曾。
? ? ? ? 全局執(zhí)行環(huán)境:是最外圍的一個(gè)執(zhí)行環(huán)境,在web瀏覽器中可以認(rèn)為是window對(duì)象漾根,因此所有的全局變量和函數(shù)都是作為window對(duì)象的屬性和方法創(chuàng)建的泰涂。代碼載入瀏覽器時(shí),全局環(huán)境被創(chuàng)建辐怕,關(guān)閉網(wǎng)頁(yè)或關(guān)閉瀏覽器時(shí)全局環(huán)境被銷毀逼蒙。
? ? ? ? 函數(shù)執(zhí)行環(huán)境:每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境,當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí)寄疏,函數(shù)的環(huán)境就被推入一個(gè)環(huán)境棧中是牢,當(dāng)函數(shù)執(zhí)行完畢后,棧將其環(huán)境彈出陕截,把控制權(quán)返回給之前的執(zhí)行環(huán)境驳棱。
? ? ? ? 每個(gè)函數(shù)運(yùn)行時(shí)都會(huì)產(chǎn)生一個(gè)執(zhí)行環(huán)境,執(zhí)行環(huán)境定義了變量和函數(shù)有權(quán)訪問(wèn)的其他數(shù)據(jù)艘策,決定了他們各自的行為蹈胡。每個(gè)執(zhí)行環(huán)境都有與之對(duì)應(yīng)的變量對(duì)象,保存著該環(huán)境中定義的所有變量和函數(shù)。
作用域鏈:
? ? ? ? 全局作用域和局部作用域中變量的訪問(wèn)權(quán)限罚渐,其實(shí)是由作用域鏈決定的却汉。
? ? ? ? 每次進(jìn)入一個(gè)新的執(zhí)行環(huán)境,都會(huì)創(chuàng)建一個(gè)用于搜索變量和函數(shù)的作用域鏈荷并。作用域鏈?zhǔn)呛瘮?shù)被創(chuàng)建的作用域中對(duì)象的集合合砂。作用域鏈可以保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)。
? ? ? ? 作用域鏈的最前端始終是當(dāng)前執(zhí)行代碼所在環(huán)境的變量對(duì)象(如果該環(huán)境是函數(shù)源织,則將其活動(dòng)對(duì)象作為變量對(duì)象)翩伪,下一個(gè)變量對(duì)象來(lái)自包含環(huán)境(包含當(dāng)前運(yùn)行環(huán)境的環(huán)境),下一個(gè)變量對(duì)象來(lái)自包含環(huán)境的包含環(huán)境谈息,依次往上缘屹,直到全局執(zhí)行環(huán)境的變量對(duì)象。全局執(zhí)行環(huán)境的變量對(duì)象始終是作用域鏈中的最后一個(gè)對(duì)象侠仇。
標(biāo)識(shí)符解析是沿著作用域一級(jí)一級(jí)的向上搜索標(biāo)識(shí)符的過(guò)程轻姿。搜素過(guò)程始終是從作用域的前端逐地向后回溯,直到找到標(biāo)識(shí)符(找不到就會(huì)導(dǎo)致錯(cuò)誤發(fā)生)逻炊。
例1:
var a = 1;
function fn1() {
function fn2() {
console.log(a); // ③互亮、執(zhí)行fn2輸出a
}
function fn3() {
var a = 4;
fn2(); // ②、fn3執(zhí)行的結(jié)果得到fn2
}
var a = 2;
return fn3; // ①余素、fn1執(zhí)行結(jié)果的得到fn3
}
var fn = fn1(); // fn1賦值fn豹休,fn1執(zhí)行結(jié)果為fn3
fn(); // 2
解析:
fn()執(zhí)行結(jié)果為fn1,fn1執(zhí)行結(jié)果為fn3桨吊,fn3執(zhí)行結(jié)果為fn2威根,fn2執(zhí)行結(jié)果是輸出a的值。
在函數(shù)fn2()執(zhí)行環(huán)境中沒(méi)有變量a屏积,解析器沿著函數(shù)fn2()的作用域鏈一級(jí)級(jí)向后回溯查找變量a医窿,在父環(huán)境即函數(shù)fn1()中找到變量a,var a = 2炊林,輸出在控制臺(tái)上姥卢。(由于局部變量的優(yōu)先級(jí)要高于同名的全局變量,所以全局var a = 1被覆蓋渣聚。)
結(jié)論:
- 函數(shù)的局部環(huán)境可以訪問(wèn)函數(shù)作用域中的變量独榴,也可以訪問(wèn)和操作父環(huán)境(包含環(huán)境)乃至全局環(huán)境的變量;
- 父環(huán)境只能訪問(wèn)其包換環(huán)境和自己環(huán)境中的變量和函數(shù)奕枝,不能訪問(wèn)其子環(huán)境中的變量和環(huán)境棺榔。(所以函數(shù)fn1()不能訪問(wèn)子環(huán)境fn3()中的var a = 4。)
例2:
var a = 1;
function fn1() {
function fn3() {
var a = 4;
fn2(); // ②隘道、fn3執(zhí)行結(jié)果得到fn2
}
var a = 2;
return fn3; // ①症歇、fn1執(zhí)行結(jié)果返回fn3
}
function fn2() {
console.log(a); // ③郎笆、執(zhí)行fn2輸出a
}
var fn = fn1(); // fn1賦值fn,fn1執(zhí)行結(jié)果為fn3
fn(); // 1
解析:
fn()執(zhí)行結(jié)果為fn1忘晤,fn1執(zhí)行結(jié)果為fn3宛蚓,fn3執(zhí)行結(jié)果為fn2,fn2執(zhí)行結(jié)果是輸出a的值设塔。
在函數(shù)fn2()執(zhí)行環(huán)境中沒(méi)有變量a凄吏,解析器沿著函數(shù)fn2()的作用域鏈一級(jí)級(jí)向后回溯查找變量a,在父環(huán)境(包含環(huán)境)即全局環(huán)境中找到變量a闰蛔,var a = 1痕钢,輸出在控制臺(tái)上。
總結(jié):
全局環(huán)境只能訪問(wèn)全局環(huán)境中的變量和函數(shù)序六,不能直接訪問(wèn)局部環(huán)境中的任何數(shù)據(jù)任连。(所以函數(shù)fn2()在全局環(huán)境不能直接訪問(wèn)函數(shù)fn1()內(nèi)的局部環(huán)境中的var a = 4或var a = 2。)