摘要:直到有一天關(guān)于閉包某一天,小伙伴們討論到說(shuō)關(guān)于閉包變量的問題時(shí),君指出,如果一個(gè)函數(shù)沒有引用到其所處閉包的變量,那這個(gè)變量所指向的空間將被釋放。對(duì)于的引擎而言,如,,,無(wú)論閉包中是否包含,它們都不會(huì)釋放掉那些再也引用不到的變量。
之前在祼看ECMA262-5,在說(shuō)到eval的地方,死活看不明白為什么會(huì)有一節(jié)專門扯到Direct Call to Eval:
A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:
The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".
The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.
當(dāng)時(shí)覺得寫規(guī)范的那群家伙一定是有毛病,眾所周知,eval無(wú)非是接收一個(gè)字符串,把這個(gè)字符串當(dāng)做代碼解釋執(zhí)行。
這個(gè)問題我一直不明白,然后就放著。
直到有一天.....
關(guān)于閉包某一天,小伙伴們討論到說(shuō)關(guān)于閉包變量的問題時(shí),Y君指出,如果一個(gè)函數(shù)沒有引用到其所處閉包的a變量,那這個(gè)a變量所指向的空間將被釋放。
function getClosure() { var a = 1; var b = 2; return function inner() { debugger; console.log(b); } } var func = getClosure(); func();
正如上面代碼所示,當(dāng)打開Chrome調(diào)試工具時(shí)到達(dá)debugger語(yǔ)句時(shí),我們只能訪問到b變量。試圖訪問a是會(huì)拋出ReferenceError的。
但是這真的能證明a引用已經(jīng)被釋放了嗎?如果沒被釋放的話,還不讓在調(diào)試的時(shí)候訪問,這個(gè)設(shè)計(jì)就只能說(shuō)是(嗶~)坑爹了,Y君補(bǔ)充道。
于是我寫了如下代碼做了測(cè)試:
function getClosure() { var a = 1; var b = 2; return function inner(val) { debugger; console.log(eval(val)); } } var func = getClosure(); func("a"); func("b");
在閉包中使用eval,這樣引擎就不知道我在閉包中的會(huì)引用到什么變量了。這一次到達(dá)debugger語(yǔ)句時(shí),驚奇地發(fā)現(xiàn),a和b都可以直接引用到了。難道是因?yàn)?b>eval的原因?引擎在發(fā)現(xiàn)閉包中有eval之后,就不會(huì)回收閉包中的垃圾了?(因?yàn)樗鼰o(wú)從得知哪些變量會(huì)在將來(lái)被引用到)。
但如果引擎不知道將來(lái)會(huì)不會(huì)執(zhí)行到eval呢?
function getClosure(){ var a = 1; var b = 2; return function inner(func, val) { debugger; console.log(func(val)); } } var f = getClosure(); f(eval, a);
把eval當(dāng)做函數(shù)傳進(jìn)去,然后讓這個(gè)eval函數(shù)解釋執(zhí)行傳入的val指向的變量。遺憾的是,到在debugger處,無(wú)法訪問到a和b,執(zhí)行到console.log(func(val))拋了異常,表示找不到引用。
于是我才回過(guò)神,似乎之前閱讀過(guò)蛇精病般的什么direct call to eval的東西,跟這個(gè)相關(guān)?
回到eval在規(guī)范中指出,進(jìn)調(diào)用eval函數(shù)時(shí):
如果是直接調(diào)用eval函數(shù)的話,將當(dāng)前的this指向,詞法環(huán)境以及變量環(huán)境當(dāng)做新的執(zhí)行上下文的this指向,詞法環(huán)境以及變量環(huán)境。
如果不是直接調(diào)用的話,則新的執(zhí)行上下文就相當(dāng)于全局執(zhí)行上下文。
>更準(zhǔn)確地說(shuō),則是以global對(duì)象,全局詞法環(huán)境以及全局變量環(huán)境當(dāng)做新的執(zhí)行上下文的this指向,詞法環(huán)境以及變量環(huán)境。
那什么是直接調(diào)用呢?參照篇首。簡(jiǎn)言之,有如下限制:
a. BaseValue必須是一個(gè)Environment Record
b. Reference Name必須是"eval"
如下例子:
var a = eval; a("hello"); //非直接調(diào)用,因?yàn)镽eference Name是"a"而不是"eval" var f = {eval: eval}; f.eval("hello"); //非直接調(diào)用,因?yàn)锽aseValue是f變量,而不是Environment Record eval("hello"); //直接調(diào)用 function test() { var eval = window.eval, x = window.eval; eval("hello"); //直接調(diào)用 x("hello"); //非直接調(diào)用 }
看到規(guī)律了吧?只要是祼的eval調(diào)用就是direct call,前面不要有宿主對(duì)象,且函數(shù)名一定要是eval字符串。
再回到閉包了解了什么是直接eval調(diào)用后,如果我弄出這樣的代碼呢?
function getClosure() { var a = 1; var b = 2; var eval = function(x){ return x; } return function inner(val) { debugger; console.log(eval(val)); } } var func = getClosure(); func("a"); func("b");
在getClosure中給一eval賦成一個(gè)完全不相干的函數(shù),進(jìn)入到debugger時(shí),能不能訪問到a和b這兩個(gè)變量呢?思考一下。
最后從eval可以管窺出來(lái),ES5的規(guī)范幾乎是從引擎現(xiàn)實(shí)者的角度來(lái)考慮問題。如果上下文中沒有eval的話,那么閉包中的變量將被很好的釋放掉(并不完全正確,參見A surprising JavaScript memory leak found at Meteor),因?yàn)橐婵梢詸z測(cè)出哪些變量會(huì)被引用到,而哪些不會(huì)。
對(duì)于ES3的引擎而言,如IE6,IE7, IE8,無(wú)論閉包中是否包含eval,它們都不會(huì)釋放掉那些再也引用不到的變量。原因在于,ES3規(guī)范中沒有對(duì)非直接eval調(diào)用進(jìn)行規(guī)范,如下代碼在IE6,7,8能通過(guò),而在現(xiàn)代瀏覽器中會(huì)報(bào)錯(cuò):
function getClosure(){ var a = 1; var b = 2; return function (func, val) { alert(func(val)); } } var f = getClosure(); f(eval, "a"); f(eval, "b");結(jié)論
不要在閉包中用eval,許多垃圾將得不到回收。
不要采用eval當(dāng)函數(shù)名,引擎會(huì)誤認(rèn)為那是一個(gè)direct call to eval的,然后內(nèi)存得不到釋放。
從理論上而言,ES5比ES3要有更好的性能,ES5更多地考慮到了內(nèi)存管理的問題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78156.html
嚴(yán)格模式 首先來(lái)了解一下嚴(yán)格模式是什么?嚴(yán)格模式是JavaScript中的一種限制性更強(qiáng)的變種方式,不是一個(gè)子集:它在語(yǔ)義上與正常代碼有明顯的差異,不支持嚴(yán)格模式的瀏覽器與支持嚴(yán)格模式的瀏覽器行為上也不一樣,所以不要在未經(jīng)嚴(yán)格模式特性測(cè)試情況下使用嚴(yán)格模式,嚴(yán)格模式可以與非嚴(yán)格模式共存,所以腳本可以逐漸的選擇性加入嚴(yán)格模式 嚴(yán)格模式的目的 首先,嚴(yán)格模式會(huì)將JavaScript陷阱直接變成明顯的錯(cuò)...
摘要:在這次螞蟻金服的電話面試?yán)锩嬲J(rèn)識(shí)到了自己很多不足的地方吧。把字符串分割為字符串?dāng)?shù)組。從起始索引號(hào)提取字符串中指定數(shù)目的字符。通常消息包括客戶機(jī)向服務(wù)器的請(qǐng)求消息和服務(wù)器向客戶機(jī)的響應(yīng)消息。 先簡(jiǎn)短的介紹一下我自己吧,我是一個(gè)前端學(xué)習(xí)者,雖然我基礎(chǔ)知識(shí)也學(xué)了比較好,但是許久不用的知識(shí)就像流失的水,很容易就忘。在這次螞蟻金服的電話面試?yán)锩嬲J(rèn)識(shí)到了自己很多不足的地方吧。雖然在阿里內(nèi)推后的人才...
摘要:在這次螞蟻金服的電話面試?yán)锩嬲J(rèn)識(shí)到了自己很多不足的地方吧。把字符串分割為字符串?dāng)?shù)組。從起始索引號(hào)提取字符串中指定數(shù)目的字符。通常消息包括客戶機(jī)向服務(wù)器的請(qǐng)求消息和服務(wù)器向客戶機(jī)的響應(yīng)消息。 先簡(jiǎn)短的介紹一下我自己吧,我是一個(gè)前端學(xué)習(xí)者,雖然我基礎(chǔ)知識(shí)也學(xué)了比較好,但是許久不用的知識(shí)就像流失的水,很容易就忘。在這次螞蟻金服的電話面試?yán)锩嬲J(rèn)識(shí)到了自己很多不足的地方吧。雖然在阿里內(nèi)推后的人才...
摘要:用戶態(tài)不能干擾內(nèi)核態(tài)所以指令就有兩種特權(quán)指令和非特權(quán)指令不同的狀態(tài)對(duì)應(yīng)不同的指令。非特權(quán)指令所有程序均可直接使用。用戶態(tài)常態(tài)目態(tài)執(zhí)行非特權(quán)指令。 這是我今年從三月份開始,主要的大廠面試經(jīng)過(guò),有些企業(yè)面試的還沒來(lái)得及整理,可能有些沒有帶答案就發(fā)出來(lái)了,還請(qǐng)各位先思考如果是你怎么回答面試官?這篇文章會(huì)持續(xù)更新,請(qǐng)各位持續(xù)關(guān)注,希望對(duì)你有所幫助! 面試清單 平安產(chǎn)險(xiǎn) 飛豬 上汽大通 浩鯨科...
閱讀 2037·2021-09-13 10:23
閱讀 2375·2021-09-02 09:47
閱讀 3831·2021-08-16 11:01
閱讀 1252·2021-07-25 21:37
閱讀 1630·2019-08-30 15:56
閱讀 561·2019-08-30 13:52
閱讀 3154·2019-08-26 10:17
閱讀 2470·2019-08-23 18:17