摘要:延長作用域鏈下面兩種語句可以在作用域鏈的前端臨時增加一個變量對象以延長作用域鏈,
問題
今天看筆記發(fā)現(xiàn)自己之前記了一個關(guān)于同名標(biāo)識符優(yōu)先級的內(nèi)容,具體是下面這樣的:
形參優(yōu)先級高于當(dāng)前函數(shù)名,低于內(nèi)部函數(shù)名
形參優(yōu)先級高于arguments
形參優(yōu)先級高于只聲明卻未賦值的局部變量,但是低于聲明且賦值的局部變量
函數(shù)和變量都會聲明提升,函數(shù)名和變量名同名時,函數(shù)名的優(yōu)先級要高。執(zhí)行代碼時,同名函數(shù)會覆蓋只聲明卻未賦值的變量,但是它不能覆蓋聲明且賦值的變量
局部變量也會聲明提升,可以先使用后聲明,不影響外部同名變量
然后我就想,為什么會有這樣的優(yōu)先級呢,規(guī)定的?但是好像沒有這個規(guī)定,于是開始查閱資料,就有了下文
初識Execution ContextExecution Context是Javascript中一個抽象概念,它定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。為了便于理解,我們可以近似將其等同于執(zhí)行當(dāng)前代碼的環(huán)境,JavaScript的可執(zhí)行代碼包括
全局代碼
函數(shù)代碼
eval()代碼
每當(dāng)執(zhí)行流轉(zhuǎn)到這些可執(zhí)行代碼時,就會“新建”一個Execution Context并進(jìn)入該Execution Context
在上圖中,共有4個Execution Context,其中有一個是Global Execution Context(有且僅有一個),還有三個Function Execution Context
再識Execution Context Stack瀏覽器中的JavaScript解釋器是單線程的,每次創(chuàng)建并進(jìn)入一個新的Execution Context時,這個Execution Context就會被推(push)進(jìn)一個環(huán)境棧中,這個棧稱為Execution Context Stack,當(dāng)當(dāng)前Execution Context的代碼執(zhí)行完之后,棧又會將其彈(pop)出,并銷毀這個Execution Context,保存在其中的變量及函數(shù)定義也隨之被銷毀,然后把控制權(quán)返回給之前的Execution Context(Global Execution Context例外,它要等到應(yīng)用程序退出后 —— 如關(guān)閉網(wǎng)頁或瀏覽器 —— 才會被銷毀)
JavaScript的執(zhí)行流就是由這個機(jī)制控制的,以下面的代碼為例說明:
var sayHello = "Hello"; function name(){ var fisrtName = "Cao", lastName = "Cshine"; function getFirstName(){ return fisrtName; } function getLatName(){ return lastName; } console.log(sayHello + getFirstName() + " " + getLastName()); } name();
當(dāng)瀏覽器第一次加載script的時候,默認(rèn)會進(jìn)入Global Execution Context,所以Global Execution Context永遠(yuǎn)是在棧的最下面。
然后遇到函數(shù)調(diào)用name(),此時新建并進(jìn)入Function Execution Context name,Function Execution Context name入棧;
繼續(xù)執(zhí)行遇到函數(shù)調(diào)用getFirstName(),于是新建并進(jìn)入Function Execution Context getFirstName,Function Execution Context getFirstName入棧,由于該函數(shù)內(nèi)部不會再新建其他Execution Context,所以直接執(zhí)行完畢,然后出棧,控制權(quán)交給Function Execution Context name;
再往下執(zhí)行遇到函數(shù)調(diào)用getLastName(),于是新建并進(jìn)入Function Execution Context getLastName,Function Execution Context getLastName入棧,由于該函數(shù)內(nèi)部不會再新建其他Execution Context,所以直接執(zhí)行完畢,然后出棧,控制權(quán)交給Function Execution Context name;
執(zhí)行完console后,函數(shù)name也執(zhí)行完畢,于是出棧,控制權(quán)交給Function Execution Context name,至此棧中又只有Global Execution Context了
關(guān)于Execution Context Stack有5個關(guān)鍵點:
單線程
同步執(zhí)行(非異步)
1個Global Execution Context
無限制的函數(shù)Function Execution Context
每個函數(shù)調(diào)用都會創(chuàng)建新的Execution Context,即使是自己調(diào)用自己,如下面的代碼:
(function foo(i) { if (i === 3) { return; } else { foo(++i); } }(0));
Execution Context Stack的情況如下圖所示:
親密接觸Execution Context每個Execution Context在概念上可以看成由下面三者組成:
變量對象(Variable object,簡稱VO)
作用域鏈(Scope Chain)
this
變量對象(Variable object)該對象與Execution Context相關(guān)聯(lián),保存著Execution Context中定義的所有變量、函數(shù)聲明以及函數(shù)形參,這個對象我們無法訪問,但是解析器在后臺處理數(shù)據(jù)是用到它(注意函數(shù)表達(dá)式以及沒用var/let/const聲明的變量不在VO中)
Global Execution Context中的變量對象VO根據(jù)宿主環(huán)境的不同而不同,在瀏覽器中為window對象,因此所有的全局變量和函數(shù)都是作為window對象的屬性和方法創(chuàng)建的。
對于Function Execution Context,變量對象VO為函數(shù)的活動對象,活動對象是在進(jìn)入Function Execution Context時創(chuàng)建的,它通過函數(shù)的arguments屬性初始化,也就是最初只包含arguments這一個屬性。
在JavaScript解釋器內(nèi)部,每次調(diào)用Execution Context都會經(jīng)歷下面兩個階段:
創(chuàng)建階段(發(fā)生在函數(shù)調(diào)用時,但是內(nèi)部代碼執(zhí)行前,這將解釋聲明提升現(xiàn)象)
創(chuàng)建作用域鏈(作用域鏈見下文)
創(chuàng)建變量對象VO
確定this的值
激活/代碼執(zhí)行階段
變量賦值、執(zhí)行代碼
其中創(chuàng)建階段的第二步創(chuàng)建變量對象VO的過程可以理解成下面這樣:
(Global Execution Context中沒有這一步) 創(chuàng)建arguments對象,掃描函數(shù)的所有形參,并將形參名稱 和對應(yīng)值組成的鍵值對作為變量對象VO的屬性。如果沒有傳遞對應(yīng)的實參,將undefined作為對應(yīng)值。如果形參名為arguments,將覆蓋arguments對象
掃描Execution Context中所有的函數(shù)聲明(注意是函數(shù)聲明,函數(shù)表達(dá)式不算)
將函數(shù)名和對應(yīng)值(指向內(nèi)存中該函數(shù)的引用指針)組成組成的鍵值對作為變量對象VO的屬性
如果變量對象VO已經(jīng)存在同名的屬性,則覆蓋這個屬性
掃描Execution Context中所有的變量聲明
由變量名和對應(yīng)值(此時為undefined) 組成,作為變量對象的屬性
如果變量名與已經(jīng)聲明的形參或函數(shù)相同,此時什么都不會發(fā)生,變量聲明不會干擾已經(jīng)存在的這個同名屬性。
好~~現(xiàn)在我們來看代碼捋一遍:
function foo(num) { console.log(num);// 66 console.log(a);// undefined console.log(b);// undefined console.log(fc);// f function fc() {} var a = "hello"; var b = function fb() {}; function fc() {} } foo(66);
當(dāng)調(diào)用foo(66)時,創(chuàng)建階段時,Execution Context可以理解成下面這個樣子
fooExecutionContext = { scopeChain: { ... }, variableObject: { arguments: { 0: 66, length: 1 }, num: 66, fc: pointer to function fc() a: undefined, b: undefined }, this: { ... } }
當(dāng)創(chuàng)建階段完成以后,執(zhí)行流進(jìn)入函數(shù)內(nèi)部,激活執(zhí)行階段,然后代碼完成執(zhí)行,Execution Context可以理解成下面這個樣子:
fooExecutionContext = { scopeChain: { ... }, variableObject: { arguments: { 0: 66, length: 1 }, num: 66, fc: pointer to function fc() a: "hello", b: pointer to function fb() }, this: { ... } }作用域鏈(Scope Chain)
當(dāng)代碼在一個Execution Context中執(zhí)行時,就會創(chuàng)建變量對象的一個作用域鏈,作用域鏈的用途是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問
Global Execution Context中的作用域鏈只有Global Execution Context的變量對象(也就是window對象),而Function Execution Context中的作用域鏈還會有“父”Execution Context的變量對象,這里就會要牽扯到[[Scopes]]屬性,可以將函數(shù)作用域鏈理解為---- 當(dāng)前Function Execution Context的變量對象VO(也就是該函數(shù)的活動對象AO) + [[Scopes]],怎么理解呢,我們繼續(xù)往下看
[[Scopes]]屬性[[Scopes]]這個屬性與函數(shù)的作用域鏈有著密不可分的關(guān)系,JavaScript中每個函數(shù)都表示為一個函數(shù)對象,[[Scopes]]是函數(shù)對象的一個內(nèi)部屬性,只有JavaScript引擎可以訪問。
結(jié)合函數(shù)的生命周期:
函數(shù)定義
[[Scopes]]屬性在函數(shù)定義時被存儲,保持不變,直至函數(shù)被銷毀
[[Scopes]]屬性鏈接到定義該函數(shù)的作用域鏈上,所以他保存的是所有包含該函數(shù)的 “父/祖父/曾祖父...” Execution Context的變量對象(OV),我們將其稱為所有父變量對象(All POV)
!!!特別注意 [[Scopes]]是在定義一個函數(shù)的時候決定的
函數(shù)調(diào)用
函數(shù)調(diào)用時,會創(chuàng)建并進(jìn)入一個新的Function Execution Context,根據(jù)前面討論過的調(diào)用Function Execution Context的兩個階段可知:先創(chuàng)建作用域鏈,這個創(chuàng)建過程會將該函數(shù)對象的[[Scopes]]屬性加入到其中
然后會創(chuàng)建該函數(shù)的活動對象AO(作為該Function Execution Context的變量對象VO),并將創(chuàng)建的這個活動對象AO加到作用域鏈的最前端
然后確定this的值
正式執(zhí)行函數(shù)內(nèi)的代碼
通過上面的過程我們大概可以理解:作用域鏈 = 當(dāng)前Function Execution Context的變量對象VO(也就是該函數(shù)的活動對象AO) + [[Scopes]],有了這個作用域鏈, 在發(fā)生標(biāo)識符解析的時候, 就會沿著作用域鏈一級一級地搜索標(biāo)識符,最開始是搜索當(dāng)前Function Execution Context的變量對象VO,如果沒有找到,就會根據(jù)[[Scopes]]找到父變量對象,然后繼續(xù)搜索該父變量對象中是否有該標(biāo)識符;如果仍沒有找到,便會找到祖父變量對象并搜索其中是否有該標(biāo)識符;如此一級級的搜索,直至找到標(biāo)識符為止(如果直到最后也找不到,一般會報未定義的錯誤);注意:對于this與arguments,只會搜到其本身的變量(活動)對象為止,而不會繼續(xù)按著作用域鏈搜素。
現(xiàn)在再結(jié)合例子來捋一遍:
var a = 10; function foo(d) { var b = 20; function bar() { var c = 30; console.log(a + b + c + d); // 110 //這里可以訪問a,b,c,d } //這里可以訪問a,b,d 但是不能訪問c bar(); } //這里只能訪問a foo(50);
當(dāng)瀏覽器第一次加載script的時候,默認(rèn)會進(jìn)入Global Execution Context的創(chuàng)建階段
創(chuàng)建Scope Chain(作用域鏈)
創(chuàng)建變量對象,此處為window對象。然后會掃描所有的全局函數(shù)聲明,再掃描全局變量聲明。之后該變量對象會加到Scope Chain中
確定this的值
此時Global Execution Context可以表示為:
globalEC = { scopeChain: { pointer to globalEC.VO }, VO: { a: undefined, foo: pointer to function foo(), (其他window屬性) }, this: { ... } }
接著進(jìn)入Global Execution Context的執(zhí)行階段
遇到賦值語句var a = 10,于是globalEC.VO.a = 10;
globalEC = { scopeChain: { pointer to globalEC.VO }, VO: { a: 10, foo: pointer to function foo(), (其他window屬性) }, this: { ... } }
遇到foo函數(shù)定義語句,進(jìn)入foo函數(shù)的定義階段,foo的[[Scopes]]屬性被確定
foo.[[Scopes]] = { pointer to globalEC.VO }
遇到foo(50)調(diào)用語句,進(jìn)入foo函數(shù)調(diào)用階段,此時進(jìn)入Function Execution Context foo的創(chuàng)建階段
創(chuàng)建Scope Chain(作用域鏈)
創(chuàng)建變量對象,此處為foo的活動對象。先創(chuàng)建arguments對象,然后掃描函數(shù)的所有形參,之后會掃描foo函數(shù)內(nèi)所有的函數(shù)聲明,再掃描foo函數(shù)內(nèi)的變量聲明。之后該變量對象會加到Scope Chain中
確定this的值
此時Function Execution Context foo可以表示為
fooEC = { scopeChain: { pointer to fooEC.VO, foo.[[Scopes]] }, VO: { arguments: { 0: 66, length: 1 }, b: undefined, d: 50, bar: pointer to function bar(), }, this: { ... } }
接著進(jìn)入Function Execution Context foo的執(zhí)行階段
遇到賦值語句var b = 20;,于是fooEC .VO.b = 20
fooEC = { scopeChain: { pointer to fooEC.VO, foo.[[Scopes]] }, VO: { arguments: { 0: 66, length: 1 }, b: 20, d: 50, bar: pointer to function bar(), }, this: { ... } }
遇到bar函數(shù)定義語句,進(jìn)入bar函數(shù)的定義階段,bar的[[Scopes]]`屬性被確定
bar.[[Scopes]] = { pointer to fooEC.VO, pointer to globalEC.VO }
遇到bar()調(diào)用語句,進(jìn)入bar函數(shù)調(diào)用階段,此時進(jìn)入Function Execution Context bar的創(chuàng)建階段
創(chuàng)建Scope Chain(作用域鏈)
創(chuàng)建變量對象,此處為bar的活動對象。先創(chuàng)建arguments對象,然后掃描函數(shù)的所有形參,之后會掃描foo函數(shù)內(nèi)所有的函數(shù)聲明,再掃描bar函數(shù)內(nèi)的變量聲明。之后該變量對象會加到Scope Chain中
確定this的值
此時Function Execution Context bar可以表示為
barEC = { scopeChain: { pointer to barEC.VO, bar.[[Scopes]] }, VO: { arguments: { length: 0 }, c: undefined }, this: { ... } }
接著進(jìn)入Function Execution Context bar的執(zhí)行階段
遇到賦值語句var c = 30,于是barEC.VO.c = 30;
barEC = { scopeChain: { pointer to barEC.VO, bar.[[Scopes]] }, VO: { arguments: { length: 0 }, c: 30 }, this: { ... } }
遇到打印語句console.log(a + b + c + d);,需要訪問變量a,b,c,d
通過bar.[[Scopes]].globalEC.VO.a訪問得到a=10
通過bar.[[Scopes]].fooEC.VO.b,bar.[[Scopes]].fooEC.VO.d訪問得到b=20,d=50
通過barEC.VO.c訪問得到c=30
通過運算得出結(jié)果110
bar函數(shù)執(zhí)行完畢,Function Execution Context bar銷毀,變量c也隨之銷毀
foo函數(shù)執(zhí)行完畢,Function Execution Context foo銷毀,b,d,bar也隨之銷毀
所有代碼執(zhí)行完畢,等到該網(wǎng)頁被關(guān)閉或者瀏覽器被關(guān)閉,Global Execution Context才銷毀,a,foo才會銷毀
通過上面的例子,相信對Execution Context和作用域鏈的理解也更清楚了,下面簡單總結(jié)一下作用域鏈:
作用域鏈的前端始終是當(dāng)前執(zhí)行的代碼所在Execution Context的變量對象;
下一個變量對象來自其包含Execution Context,以此類推;
最后一個變量對象始終是Global Execution Context的變量對象;
內(nèi)部Execution Context可通過作用域鏈訪問外部Execution Context;反之不可以;
標(biāo)識符解析是沿著作用域鏈一級一級地搜索標(biāo)識符的過程。搜索過程始終從作用域鏈的前端開始,然后逐級的向后回溯,直到找到標(biāo)識符為止(如果找不到,通常會導(dǎo)致錯誤);
作用域鏈的本質(zhì)是一個指向變量對象的指針列表,只引用而不實際包含變量對象。
延長作用域鏈下面兩種語句可以在作用域鏈的前端臨時增加一個變量對象以延長作用域鏈,該變量對象會在代碼執(zhí)行后被移除
try-catch語句的catch塊
創(chuàng)建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明
with語句
將指定的對象添加到作用域鏈中
function buildUrl(){ var qs = "?debug=true"; with(location){ var url = href + qs; } //console.log(href) 將會報href is not defined的錯誤,因為with語句執(zhí)行完with創(chuàng)建的變量對象就被移除了 return url; }
with語句接收window.location對象,因此其變量對象就包含了window.location對象的所有屬性,而這個變量對象被添加到作用域鏈的前端。所以在with語句里面使用href相當(dāng)于window.location.href。
解答問題現(xiàn)在我們來解答最開始的優(yōu)先級問題
形參優(yōu)先級高于當(dāng)前函數(shù)名,低于內(nèi)部函數(shù)名
function fn(fn){ console.log(fn);// cc } fn("cc");
函數(shù)fn屬于Global Execution Context,而形參fn屬于Function Execution Context fn,此時作用域的前端是Function Execution Context fn的變量對象,所以console.log(fn)為形參的值
function fa(fb){ console.log(fb);// ? fb(){} function fb(){} console.log(fb);// ? fb(){} } fa("aaa");
調(diào)用fa函數(shù)時,進(jìn)入Function Execution Context fa的創(chuàng)建階段,根據(jù)前面所說的變量對象創(chuàng)建過程:
先創(chuàng)建arguments對象,然后掃描函數(shù)的所有形參,之后會掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
掃描函數(shù)聲明時,如果變量對象VO中已經(jīng)存在同名的屬性,則覆蓋這個屬性
我們可以得到fa的變量對象表示為:
fa.VO = { arguments: { 0:"aaa", length: 1 }, fb: pointer to function fb(), }
所以console.log(fb)得到的是fa.VO.fb的值? fb(){}
形參優(yōu)先級高于arguments
function fn(aa){ console.log(arguments);// Arguments?["hello world"] } fn("hello world"); function fn(arguments){ console.log(arguments);// hello world } fn("hello world");
調(diào)用fn函數(shù)時,進(jìn)入Function Execution Context fn的創(chuàng)建階段,根據(jù)前面所說的變量對象創(chuàng)建過程:
先創(chuàng)建arguments對象,然后掃描函數(shù)的所有形參,之后會掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
先創(chuàng)建arguments對象,后掃描函數(shù)形參,如果形參名為arguments,將會覆蓋arguments對象
所以當(dāng)形參名為arguments時,console.log(arguments)為形參的值hello world。
形參優(yōu)先級高于只聲明卻未賦值的局部變量,但是低于聲明且賦值的局部變量
function fa(aa){ console.log(aa);//aaaaa var aa; console.log(aa);//aaaaa } fa("aaaaa");
調(diào)用fa函數(shù)時,進(jìn)入Function Execution Context fa的創(chuàng)建階段,根據(jù)前面所說的變量對象創(chuàng)建過程:
先創(chuàng)建arguments對象,然后掃描函數(shù)的所有形參,之后會掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
掃描函數(shù)內(nèi)的變量聲明時,如果變量名與已經(jīng)聲明的形參或函數(shù)相同,此時什么都不會發(fā)生,變量聲明不會干擾已經(jīng)存在的這個同名屬性
所以創(chuàng)建階段之后Function Execution Context fa的變量對象表示為:
fa.VO = { arguments: { 0:"aaaaa", length: 1 }, aa:"aaaaa", }
之后進(jìn)入Function Execution Context fa的執(zhí)行階段:console.log(aa);打印出fa.VO.aa(形參aa)的值aaaaa;由于var aa;僅聲明而未賦值,所以不會改變fa.VO.aa的值,所以下一個console.log(aa);打印出的仍然是fa.VO.aa(形參aa)的值aaaaa。
function fb(bb){ console.log(bb);//bbbbb var bb = "BBBBB"; console.log(bb);//BBBBB } fb("bbbbb");
調(diào)用fb函數(shù)時,進(jìn)入Function Execution Context fb的創(chuàng)建階段,根據(jù)前面所說的變量對象創(chuàng)建過程:
先創(chuàng)建arguments對象,然后掃描函數(shù)的所有形參,之后會掃描函數(shù)內(nèi)所有的函數(shù)聲明,再掃描函數(shù)內(nèi)的變量聲明;
掃描函數(shù)內(nèi)的變量聲明時,如果變量名與已經(jīng)聲明的形參或函數(shù)相同,此時什么都不會發(fā)生,變量聲明不會干擾已經(jīng)存在的這個同名屬性
所以創(chuàng)建階段之后Function Execution Context fb的變量對象表示為:
fb.VO = { arguments: { 0:"bbbbb", length: 1 }, bb:"bbbbb", }
之后進(jìn)入Function Execution Context fb的執(zhí)行階段:console.log(bb);打印出fb.VO.bb(形參bb)的值"bbbbb";遇到var bb = "BBBBB";,fb.VO.bb的值將被賦為BBBBB,所以下一個console.log(bb);打印出fb.VO.bb(局部變量bb)的值BBBBB。
函數(shù)和變量都會聲明提升,函數(shù)名和變量名同名時,函數(shù)名的優(yōu)先級要高。
console.log(cc);//? cc(){} var cc = 1; function cc(){}
根據(jù)Global Execution Context的創(chuàng)建階段中創(chuàng)建變量對象的過程:是先掃描函數(shù)聲明,再掃描變量聲明,且變量聲明不會影響已存在的同名屬性。所以在遇到var cc = 1;這個聲明語句之前,global.VO.cc為? cc(){}。
執(zhí)行代碼時,同名函數(shù)會覆蓋只聲明卻未賦值的變量,但是它不能覆蓋聲明且賦值的變量
var cc = 1; var dd; function cc(){} function dd(){} console.log(cc);//1 console.log(dd);//? dd(){}
Global Execution Context的創(chuàng)建階段之后,Global Execution Context的變量對象可以表示為:
global.VO = { cc:pointer to function cc(), dd:pointer to function dd() }
然后進(jìn)入Global Execution Context的執(zhí)行階段,遇到var cc = 1;這個聲明賦值語句后, global.VO.cc將被賦值為1;然后再遇到var dd這個聲明語句,由于僅聲明未賦值,所以不改變global.VO.dd的值;所以console.log(cc);打印出1,console.log(dd);打印出? dd(){}
局部變量也會聲明提升,可以先使用后聲明,不影響外部同名變量
每個Execution Context都會有變量創(chuàng)建這個過程,所以會有聲明提升;根據(jù)作用域鏈,如果局部變量與外部變量同名,那么最先找到的是局部變量,影響不到外部同名變量
相關(guān)資料JavaScript基礎(chǔ)系列---變量及其值類型
Understanding Scope in JavaScript
What is the Execution Context & Stack in JavaScript?
深入探討JavaScript的執(zhí)行環(huán)境和棧
作用域原理
JavaScript執(zhí)行環(huán)境 + 變量對象 + 作用域鏈 + 閉包
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/95064.html
摘要:之前一篇文章我們詳細(xì)說明了變量對象,而這里,我們將詳細(xì)說明作用域鏈。而的作用域鏈,則同時包含了這三個變量對象,所以的執(zhí)行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當(dāng)前的函數(shù)調(diào)用棧,為當(dāng)前正在被執(zhí)行的函數(shù)的作用域鏈,為當(dāng)前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學(xué)JavaScrip...
摘要:所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。所以本文中將以維基百科中的定義為準(zhǔn)即在計算機(jī)科學(xué)中,閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。 閉包(closure)是JavaScript中一個神秘的概念,許多人都對它難以理解,我也一直處于似懂非懂的狀態(tài),前幾天深入了解了一下執(zhí)行環(huán)境以及作用域鏈,可戳查看詳情,而閉包與作用域及作用域鏈的關(guān)系密不可分,所...
摘要:所以覺得把這個執(zhí)行的詳細(xì)過程整理一下,幫助更好的理解。類似的語法報錯的如下圖所示三預(yù)編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進(jìn)入預(yù)編譯階段。另開出新文章詳細(xì)分析,主要介紹執(zhí)行階段中的同步任務(wù)執(zhí)行和異步任務(wù)執(zhí)行機(jī)制事件循環(huán)。 一、概述 js是一種非常靈活的語言,理解js引擎的執(zhí)行過程對于我們學(xué)習(xí)js是非常有必要的。看了很多這方便文章,大多數(shù)是講的是事件循環(huán)(event loo...
摘要:變量對象作用域鏈因為變量對象在執(zhí)行上下文進(jìn)入執(zhí)行階段時,就變成了活動對象,因此圖中使用了來表示。 作用域 作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在 JavaScript 中,變量的作用域有全局作用域和局部作用域兩種。JavaScript 采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。 靜態(tài)作用域 函數(shù)的作用域在函數(shù)定義的時候...
閱讀 4020·2021-11-22 13:53
閱讀 3617·2021-11-19 11:29
閱讀 1265·2021-09-08 09:35
閱讀 3162·2020-12-03 17:26
閱讀 514·2019-08-29 16:06
閱讀 2106·2019-08-26 13:50
閱讀 1180·2019-08-23 18:32
閱讀 2153·2019-08-23 18:12