一個JS面試題

---只有程序員才懂的幽默---
一程序員去面試史简,面試官問:“你畢業(yè)才兩面乃秀,這三年工作經(jīng)驗(yàn)是怎么來的肛著?!”程序員回答:“加班跺讯∈嗷撸”

最近一段時間呢,被一個JS面試題刷屏了刀脏,接下來我們也簡單談一下這道JS面試題所引發(fā)的思考局荚。
題目是這樣的:

//比較下面兩段代碼,試述兩段代碼的不同之處
// A--------------------------
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope(); 
// B---------------------------
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

首先A愈污、B兩段代碼輸出返回的都是 “l(fā)ocal scope”耀态,如果對這一點(diǎn)還有疑問的同學(xué)請自覺回去溫習(xí)一下js作用域的相關(guān)知識。
接下來我們分析下這兩段代碼的執(zhí)行過程:
首先是A:

進(jìn)入全局環(huán)境上下文暂雹,全局環(huán)境被壓入環(huán)境棧首装,contextStack = [globalContext]
全局上下文環(huán)境初始化,

globalContext={
    variable object:[scope, checkscope],
    scope chain: variable object // 全局作用域鏈
}

,同時checkscope函數(shù)被創(chuàng)建杭跪,此時 checkscope.[[Scope]] = globalContext.scopeChain
執(zhí)行checkscope函數(shù)仙逻,進(jìn)入checkscope函數(shù)上下文,checkscope被壓入環(huán)境棧涧尿,contextStack=[checkscopeContext, globalContext]系奉。隨后checkscope上下文被初始化,它會復(fù)制checkscope函數(shù)的[[Scope]]變量構(gòu)建作用域,即 checkscopeContext={ scopeChain : [checkscope.[[Scope]]] }
checkscope的活動對象被創(chuàng)建 此時 checkscope.activationObject = [arguments], 隨后活動對象被當(dāng)做變量對象用于初始化现斋,checkscope.variableObject = checkscope.activationObject = [arguments, scope, f]喜最,隨后變量對象被壓入checkscope作用域鏈前端,(checckscope.scopeChain = [checkscope.variableObject, checkscope.[[Scope]] ]) == [[arguments, scope, f], globalContext.scopeChain]
函數(shù)f被初始化庄蹋,f.[[Scope]] = checkscope.scopeChain。
checkscope執(zhí)行流繼續(xù)往下走到 return f()迷雪,進(jìn)入函數(shù)f執(zhí)行上下文限书。函數(shù)f執(zhí)行上下文被壓入環(huán)境棧,contextStack = [fContext, checkscopeContext, globalContext]章咧。函數(shù)f重復(fù) 第4步 動作倦西。最后 f.scopeChain = [f.variableObject,checkscope.scopeChain]
函數(shù)f執(zhí)行完畢,f的上下文從環(huán)境棧中彈出赁严,此時 contextStack = [checkscopeContext, globalContext]扰柠。同時返回 scope, 解釋器根據(jù)f.scopeChain查找變量scope,在checkscope.scopeChain中找到scope(local scope)。
checkscope函數(shù)執(zhí)行完畢疼约,其上下文從環(huán)境棧中彈出卤档,contextStack = [globalContext]
如果你理解了A的執(zhí)行流程,那么B的流程在細(xì)節(jié)上一致程剥,唯一的區(qū)別在于B的環(huán)境棧變化不一樣劝枣,

A: contextStack = [globalContext] —> contextStack = [checkscopeContext, globalContext] —> contextStack = [fContext, checkscopeContext, globalContext] —> contextStack = [checkscopeContext, globalContext] —> contextStack = [globalContext]

B: contextStack = [globalContext] —> contextStack = [checkscopeContext, globalContext] —> contextStack = [fContext, globalContext] —> contextStack = [globalContext]

也就是說,真要說這兩段代碼有啥不同,那就是他們執(zhí)行過程中環(huán)境棧的變化不一樣舔腾,其他的兩種方式都一樣溪胶。

其實(shí)對于理解這兩段代碼而言最根本的一點(diǎn)在于,javascript是使用靜態(tài)作用域的語言稳诚,他的作用域在函數(shù)創(chuàng)建的時候便已經(jīng)確定(不含arguments)哗脖。

說了這么一大坨偏理論的東西,能堅(jiān)持看下來的同學(xué)估計(jì)都要睡著了…是的扳还,這么一套理論性的東西糾結(jié)有什么用呢才避,我只要知道函數(shù)作用域在創(chuàng)建時便已經(jīng)生成不就好了么。沒有實(shí)踐價值的理論往往得不到重視普办。那我們來看看工扎,當(dāng)我們了解到這一套理論之后我們的世界到底會發(fā)生了什么變化:
這樣一段代碼:

function setFirstName(firstName){
 
    return function(lastName){
        return firstName+" "+lastName;
    }
}
 
var setLastName = setFirstName("kuitos");
var name = setLastName("lau");
 
// 調(diào)用setFirstName函數(shù)時返回一個匿名函數(shù),該匿名函數(shù)會持有setFirstName函數(shù)作用域的變量對象(里面包含arguments和firstName)衔蹲,不管匿名函數(shù)是否會使用該變量對象里的信息肢娘,這個持有邏輯均不會改變。
// 也就是當(dāng)setFirstName函數(shù)執(zhí)行完之后其執(zhí)行環(huán)境被銷毀舆驶,但是他的變量對象會一直保存在內(nèi)存中不被銷毀(因?yàn)楸荒涿瘮?shù)hold)橱健。同樣的,垃圾回收機(jī)制會因?yàn)樽兞繉ο蟊灰恢県old而不做回收處理沙廉。這個時候內(nèi)存泄露就發(fā)生了拘荡。這時候我們需要做手動釋放內(nèi)存的處理。like this:
setLastName = null;
// 由于匿名函數(shù)的引用被置為null撬陵,那么其hold的setFirstName的活動對象就能被安全回收了珊皿。
// 當(dāng)然,現(xiàn)代瀏覽器引擎(以V8為首)都會嘗試回收閉包所占用的內(nèi)存巨税,所以這一點(diǎn)我們也不必過多處理蟋定。
                                                  2016-11-30 00:58:53
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市草添,隨后出現(xiàn)的幾起案子驶兜,更是在濱河造成了極大的恐慌,老刑警劉巖远寸,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抄淑,死亡現(xiàn)場離奇詭異,居然都是意外死亡驰后,警方通過查閱死者的電腦和手機(jī)肆资,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倡怎,“玉大人迅耘,你說我怎么就攤上這事贱枣。” “怎么了颤专?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵纽哥,是天一觀的道長。 經(jīng)常有香客問我栖秕,道長春塌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任簇捍,我火速辦了婚禮只壳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘暑塑。我一直安慰自己吼句,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布事格。 她就那樣靜靜地躺著惕艳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驹愚。 梳的紋絲不亂的頭發(fā)上远搪,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音逢捺,去河邊找鬼谁鳍。 笑死,一個胖子當(dāng)著我的面吹牛劫瞳,可吹牛的內(nèi)容都是我干的倘潜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼志于,長吁一口氣:“原來是場噩夢啊……” “哼窍荧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恨憎,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎郊楣,沒想到半個月后憔恳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡净蚤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年钥组,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片今瀑。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡程梦,死狀恐怖点把,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屿附,我是刑警寧澤郎逃,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站挺份,受9級特大地震影響褒翰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匀泊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一优训、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧各聘,春花似錦揣非、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毛仪,卻和暖如春搁嗓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箱靴。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工腺逛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衡怀。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓棍矛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抛杨。 傳聞我的和親對象是個殘疾皇子够委,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容