您好,登錄后才能下訂單哦!
好程序員 web前端帶 你了解 JS的作用域鏈, 我們都知道 js 是一個(gè)基于對(duì)象的語(yǔ)言,系統(tǒng)內(nèi)置各種對(duì)象。
而 window 作為一個(gè)天然存在的全局對(duì)象,它承擔(dān)了所有全局資源的存儲(chǔ)。
我們使用的任何全局變量,都是 window 下的。也就是說(shuō),在 js 中,實(shí)際上沒(méi)有任何對(duì)象、方法是可以獨(dú)立的,它們必須依賴于某個(gè)對(duì)象才可以被訪問(wèn)或執(zhí)行。
就像 alert() ,它的完整寫法是 window.alert()
parseInt(), 完整寫法是 window.parseInt()
所有放在 window 對(duì)象下的資源,訪問(wèn)時(shí)可以默認(rèn)省略 window
但有一種情況非常特殊,例如函數(shù)中的局部變量:
function Person(){
var name = “abc” ;
}
當(dāng)我們?cè)噲D訪問(wèn)這個(gè) name 屬性時(shí)
console.log(newPerson().name);
結(jié)果是 undefined
我們只能在函數(shù)內(nèi)部訪問(wèn):
function Person(){
var name = “abc”;
console .log(name);
}
這種屬性,在構(gòu)造函數(shù)中,也被稱為私有屬性,我們必須提供一種對(duì)外公開(kāi)的方法才可以被外界訪問(wèn)。例如:
function Person(){
var name = “abc” ;
this .getName = function (){
return name;
}
this .setName= function (_name){
name = _name;
}
}
這是一個(gè)典型的作用域問(wèn)題 , 似乎我們每個(gè)人都知道。
但這似乎也違反了我們的一個(gè)常識(shí):那就是在 js 中,所有資源都必須依賴對(duì)象才能存在,不可獨(dú)立使用。比如說(shuō):
function aaa(){
function bbb(){ }
bbb();
}
這段代碼看上去并沒(méi)有錯(cuò),但是請(qǐng)問(wèn) bbb 這個(gè)函數(shù)為什么可以獨(dú)立存在呢?如果我們把它換成這樣:
function aaa(){
function bbb(){ }
window .bbb();
}
結(jié)果是運(yùn)行錯(cuò)誤!
那如果換成這樣呢?
function aaa(){
function bbb(){ }
this .bbb();
}
結(jié)果還是運(yùn)行錯(cuò)誤!
那么我們不禁要發(fā)問(wèn)了, bbb 這個(gè)函數(shù)到底是屬于哪個(gè)對(duì)象的?
當(dāng)我們?cè)谡{(diào)用一個(gè)函數(shù)的時(shí)候,瀏覽器會(huì)為這個(gè)函數(shù)的執(zhí)行開(kāi)辟一塊內(nèi)存區(qū)域用來(lái)存儲(chǔ)這個(gè)方法在執(zhí)行的臨時(shí)數(shù)據(jù)。而對(duì)象作為 js 的基本存儲(chǔ)單位,因此,臨時(shí)數(shù)據(jù)實(shí)際上都被保存到了一個(gè)對(duì)象當(dāng)中,這個(gè)對(duì)象,就是我們平時(shí)所說(shuō)的 執(zhí)行上下文環(huán)境
當(dāng)我們調(diào)用 aaa 函數(shù)時(shí),例如 window.aaa()
瀏覽器會(huì)為這一次函數(shù)的執(zhí)行,創(chuàng)建一個(gè)執(zhí)行上下文環(huán)境對(duì)象,我們暫時(shí)給它起個(gè)名字,叫做 contextAAA 吧,當(dāng)然它是臨時(shí)的,因?yàn)楹瘮?shù)執(zhí)行完它也就消失了。
那我們的代碼實(shí)際上會(huì)變成這樣:
function aaa(){
function bbb(){ }
contextAAA .bbb();
}
盡管 contextAAA 對(duì)象是看不見(jiàn)的,但它確實(shí)存在。
而當(dāng)我們執(zhí)行 bbb 函數(shù)時(shí),瀏覽器會(huì)再次為這一次函數(shù)調(diào)用創(chuàng)建一個(gè)臨時(shí)的執(zhí)行上下文環(huán)境,我們暫且叫它 contextBBB
那么 contextAAA 和 contextBBB以及window 之間會(huì)形成鏈條關(guān)系,
舉個(gè)例子來(lái)說(shuō)明吧
var num = 888;
function aaa(){
var num = 100;
function bbb(){
var num= 200;
console .log(num);
}
bbb();
}
aaa();
那么 contextAAA 如下:
contextAAA = {
num : 100,
bbb : function (){ … },
parentContext: window // 父級(jí)上下文對(duì)象
}
那么 contextBBB 如下:
contextBBB = {
num : 200,
parentContext: contextAAA // 父級(jí)上下文對(duì)象
}
因此我們發(fā)現(xiàn),在父級(jí)上下文對(duì)象中,我們沒(méi)有辦法訪問(wèn)到子級(jí)上下對(duì)象,這是一個(gè)單向鏈表,這就是全局不能訪問(wèn)局部的原因。
而 bbb 函數(shù)中打印出的 num 應(yīng)該是多少呢?這取決在上下文對(duì)象中的查找順序,順序大概是這樣的:
首先在當(dāng)前上下文對(duì)象 contextBBB 中,找一下有沒(méi)有 num 變量,找到就直接打印。以我們目前的代碼看,結(jié)果應(yīng)該是 200
我們把代碼改造一下:
var num= 888;
function aaa(){
var num = 100;
function bbb(){
console .log(num);
}
bbb();
}
aaa();
由于這次在 contextBBB 對(duì)象中找不到 num 變量了,因此它會(huì)從父級(jí)上下文對(duì)象中查找,也就是 contextAAA 里面的 num ,因此打印的結(jié)果是 100;
我們?cè)侔汛a改造一下
var num= 888;
function aaa(){
function bbb(){
console .log(num);
}
bbb();
}
aaa();
由于這次連 contextAAA 對(duì)象里也找不到了,會(huì)再次向它的父級(jí)上下文對(duì)象,也就是 window 查找,因此打印結(jié)果是 888
contextAAA 和 contextBBB 的父子關(guān)系,在你寫代碼的一刻就決定了,這就是作用域。
function aaa(){
var
num = 10;
function bbb(){ console .log(num); }
}
而代碼執(zhí)行時(shí),產(chǎn)生的上下文對(duì)象,是鏈表的關(guān)系。這就是我們所說(shuō)的作用域鏈,它的原理跟原型鏈?zhǔn)且粯拥摹?
理解了這一點(diǎn),也能弄明白閉包的原理。
function aaa(){
var num = 10;
return function bbb(){ console .log(num); }
}
aaa( )( )
盡管 bbb函數(shù)通過(guò)return在全局范圍被執(zhí)行了,但作用域的鏈表關(guān)系并沒(méi)有發(fā)生改變,因此,bbb函數(shù)依然可以訪問(wèn)num這個(gè)局部變量。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。