亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

javascript SpiderMonkey中的函數(shù)序列化如何進(jìn)行

 更新時(shí)間:2012年12月05日 11:45:48   作者:  
JavaScript中如何進(jìn)行函數(shù)序列化,函數(shù)序列化的作用是什么?本文將介紹SpiderMonkey中的函數(shù)序列化,有需要的朋友可以參考下
在Javascript中,函數(shù)可以很容易的被序列化(字符串化),也就是得到函數(shù)的源碼.但其實(shí)這個(gè)操作的內(nèi)部實(shí)現(xiàn)(引擎實(shí)現(xiàn))并不是你想象的那么簡(jiǎn)單.SpiderMonkey中一共使用過(guò)兩種函數(shù)序列化的技術(shù):一種是利用反編譯器(decompiler)將函數(shù)編譯后的字節(jié)碼反編譯成源碼字符串,另一種是在將函數(shù)編譯成字節(jié)碼之前就把函數(shù)源碼壓縮并存儲(chǔ)下來(lái),用到的時(shí)候再解壓還原.

如何進(jìn)行函數(shù)序列化
在SpiderMonkey中,能將函數(shù)序列化的方法或函數(shù)有三個(gè):Function.prototype.toString,Function.prototype.toSource,uneval.只有toString方法是標(biāo)準(zhǔn)的,也就是各引擎通用的.但是ES標(biāo)準(zhǔn)中關(guān)于Function.prototype.toString方法的規(guī)定(ES5 15.3.4.2)只有寥寥數(shù)語(yǔ),也就是說(shuō),基本沒(méi)有標(biāo)準(zhǔn),引擎自己決定該如何實(shí)現(xiàn).

函數(shù)序列化的作用
函數(shù)序列化最主要的作用應(yīng)該是利用序列化生成的函數(shù)源碼來(lái)重新定義這個(gè)函數(shù).
復(fù)制代碼 代碼如下:

function a() {
...
alert("a")
...
}

a() //執(zhí)行時(shí)可能會(huì)彈出"a"

a = eval("(" + a.toString().replace('alert("a")', 'alert("b")') + ")")

a() //執(zhí)行時(shí)可能會(huì)彈出"b"

你也許會(huì)想:"我寫了這么多年Javascript,怎么沒(méi)有遇到這種需求".的確,如果是自己的網(wǎng)站,自己完全控制的js文件,不需要以這種打補(bǔ)丁的方式來(lái)修改函數(shù),直接修改就可以了.但是如果源文件不是你能控制的了的話,就很有可能要這樣做了.比如常用的地方有g(shù)reasemonkey腳本:你可能需要禁用或修改某個(gè)網(wǎng)站中的某個(gè)函數(shù).還有就是Firefox擴(kuò)展:你需要修改Firefox自身的某個(gè)函數(shù)(可以說(shuō)Firefox是用JS寫的).舉個(gè)我自己寫的Firefox腳本的例子:
復(fù)制代碼 代碼如下:
location == "chrome://browser/content/browser.xul" && eval("gURLBar.handleCommand=" + gURLBar.handleCommand.toString().replace(/^\s*(load.+);/gm, "/^javascript:/.test(url)||(content.location=='about:blank'||content.location=='about:newtab')?$1:gBrowser.loadOneTab(url,{postData:postData,inBackground:false, allowThirdPartyFixup: true});"))

這個(gè)代碼的作用是:在地址欄上回車時(shí),讓Firefox在新標(biāo)簽中打開(kāi)頁(yè)面,而不是占用當(dāng)前標(biāo)簽.實(shí)現(xiàn)方式就是用toString方法讀取到gURLBar.handleCommand函數(shù)的源碼,然后用正則替換后傳給eval,重新定義了這個(gè)函數(shù).

為什么不用直接定義的方式,也就是直接重寫函數(shù)呢:

gURLBar.handleCommand = function(){...//將原本的函數(shù)更改了一個(gè)小地方}
不能這么做的原因是因?yàn)槲覀兊每紤]兼容性,我們應(yīng)該盡可能小的更改這個(gè)函數(shù)的源碼.如果這么寫的話,Firefox的gURLBar.handleCommand源碼一旦發(fā)生變化,這個(gè)腳本就失效了.比如Firefox3和Firefox4中都有這個(gè)函數(shù),但函數(shù)內(nèi)容差別非常大,可是如果用正則替換部分關(guān)鍵字的話,只要這個(gè)被替換的這個(gè)關(guān)鍵字沒(méi)有發(fā)生變化的話,就不會(huì)出現(xiàn)不兼容的現(xiàn)象.

反編譯字節(jié)碼
在SpiderMonkey中,函數(shù)在被解析之后會(huì)被編譯成字節(jié)碼(bytecode),也就是說(shuō),內(nèi)存中存儲(chǔ)著并不是原始的函數(shù)源碼.SpiderMonkey中存在一個(gè)反編譯器,它的主要作用就是把函數(shù)的字節(jié)碼反編譯成函數(shù)源碼的形式.

在Firefox16以及之前的版本中,SpiderMonkey使用的就是這種方法,如果你使用的是這些版本的Firefox的話,可以嘗試下面的代碼:
復(fù)制代碼 代碼如下:

alert(function () {
"字符串";
//注釋
return 1 + 2 + 3
}.toString())
返回的字符串是

function () {
return 6;
}

輸出和其他的瀏覽器完全不同:

1.沒(méi)有意義的原始值字面量在編譯的時(shí)候會(huì)被刪除,這個(gè)例子中就是"字符串".

你也許會(huì)覺(jué)得:"貌似沒(méi)什么問(wèn)題,反正這些值對(duì)于函數(shù)的運(yùn)行來(lái)說(shuō)并沒(méi)有什么意義".等等,你是不是忘了個(gè)東西,表示嚴(yán)格模式的字符串"use strict"怎么辦呢?

在不支持嚴(yán)格模式的版本中,比如Firefox3.6,這個(gè)"use strict"和其他字符串沒(méi)什么區(qū)別,編譯的時(shí)候會(huì)被刪除.在SpiderMonkey實(shí)現(xiàn)了嚴(yán)格模式之后,雖然編譯的時(shí)候同樣會(huì)忽略掉這個(gè)字符串"use strict",但在反編譯的時(shí)候會(huì)進(jìn)行判斷,如果這個(gè)函數(shù)處于嚴(yán)格模式中,則會(huì)在函數(shù)體的第一行添加上"use strict",下面是對(duì)應(yīng)的引擎源碼.

static JSBool
復(fù)制代碼 代碼如下:

DecompileBody(JSPrinter *jp, JSScript *script, jsbytecode *pc)
{
/* Print a strict mode code directive, if needed. */
if (script->strictModeCode && !jp->strict) {
if (jp->fun && (jp->fun->flags & JSFUN_EXPR_CLOSURE)) {
/*
* We have no syntax for strict function expressions;
* at least give a hint.
*/
js_printf(jp, "\t/* use strict */ \n");
} else {
js_printf(jp, "\t\"use strict\";\n");
}
jp->strict = true;
}

jsbytecode *end = script->code + script->length;
return DecompileCode(jp, script, pc, end - pc, 0);
}

2.注釋在編譯的時(shí)候也會(huì)被刪除

這個(gè)貌似沒(méi)太大影響,不過(guò)有些人愿意利用函數(shù)注釋來(lái)實(shí)現(xiàn)多行字符串,這個(gè)方法在Firefox 17之前的版本中是不可用的.
復(fù)制代碼 代碼如下:

function hereDoc(f) { 
return f.toString().replace(/^.+\s/,"").replace(/.+$/,"");
}
var string = hereDoc(function () {/*



*/});
console.log(string)




3.原始值字面量的運(yùn)算會(huì)在編譯時(shí)進(jìn)行.

這算是一種優(yōu)化方式,《高性能JavaScript》提到過(guò):

反編譯的弊端
由于新技術(shù)的出現(xiàn)(比如嚴(yán)格模式)以及在修改其他相關(guān)bug的時(shí)候,反編譯器這部分的實(shí)現(xiàn)經(jīng)常需要更改,更改就有可能產(chǎn)生新的bug,我自己就親身遇到過(guò)一個(gè)bug.大概是在Firefox10左右的時(shí)候,具體問(wèn)題記不大清了,反正是關(guān)于反編譯時(shí)小括號(hào)是否要保留的問(wèn)題,大概是這樣的:
復(fù)制代碼 代碼如下:

>(function (a,b,c){return (a+b)+c}).toString()
"function (a, b, c) {
return a + b + c;
}"

在反編譯時(shí),(a+b)中的小括號(hào)被省略了,由于加法結(jié)合律從左到右,所以這沒(méi)關(guān)系.但我遇到的bug是這樣的:
復(fù)制代碼 代碼如下:

>(function (a,b,c){return a+(b+c)}).toString()
"function (a, b, c) {
return a + b + c;
}"

這就就不行了,a+b+c不等于a+(b+c),比如在a=1,b=2,c="3"的情況下,a+b+c等于"33",而a+(b+c)等于"123".

關(guān)于反編譯器,Mozilla工程師Luke Wagner指出,反編譯器對(duì)他們實(shí)現(xiàn)一些新功能的阻礙很大,而且經(jīng)常會(huì)出現(xiàn)一些bug:

Not to pile on, but I too have felt an immense drag from the decompiler in the last year. Testing coverage is also poor and any non-trivial change inevitably produces fuzz bugs.The sooner we remove this drag the sooner we start reaping the benefits. In particular,I think now is a much better time to remove it than after doing significant frontend/bytecode hacking for new language features.

Brendan Eich也表示,反編譯器的確有很多不理想:

I have no love for the decompiler, it has been hacked over for 17 years. 存儲(chǔ)函數(shù)源碼
從Firefox17之后,SpiderMonkey改成了第二種實(shí)現(xiàn)方法,其他瀏覽器也應(yīng)該是這樣實(shí)現(xiàn)的吧.函數(shù)序列化得到的字符串完全和源碼一致,包括空白符,注釋等等.這樣的話,大部分問(wèn)題就應(yīng)該沒(méi)有了吧.不過(guò),貌似我又想到個(gè)問(wèn)題.還是關(guān)于嚴(yán)格模式的.

比如:
復(fù)制代碼 代碼如下:

(function A() {
"use strict";
alert("A");
}) + ""

當(dāng)然,返回的源碼中也應(yīng)該有"use strict",所有瀏覽器都是這么實(shí)現(xiàn)的:
復(fù)制代碼 代碼如下:

function A() {
"use strict";
alert("A");
}

但如果是這樣呢:
復(fù)制代碼 代碼如下:

(function A() {
"use strict";
return function B() {
alert("B")
}
})() + ""

內(nèi)部函數(shù)B也處于嚴(yán)格模式中,輸出B的函數(shù)源碼應(yīng)不應(yīng)該加上"use strict"呢.試驗(yàn)一下:

上面說(shuō)了,Firefox17之前Firefox4之后的版本是通過(guò)判斷當(dāng)前函數(shù)是否處于嚴(yán)格模式來(lái)決定輸出不輸出"use strict"的,函數(shù)B繼承了函數(shù)A的嚴(yán)格模式,所以會(huì)有"use strict".

同時(shí)函數(shù)源碼是縮進(jìn)嚴(yán)格的,因?yàn)樵诜淳幾g的時(shí)候,SpiderMonkey會(huì)給反編譯出的源碼進(jìn)行格式化,即使之前的源碼完全沒(méi)有縮進(jìn)也沒(méi)關(guān)系:
復(fù)制代碼 代碼如下:

function B() {
"use strict";
alert("B");
}

Firefox17之后的版本會(huì)不會(huì)帶有"use strict"呢?因?yàn)槭侵苯影押瘮?shù)源碼保存下來(lái)的,而且函數(shù)B中的確沒(méi)有"use strict"字樣.試驗(yàn)結(jié)果是:會(huì)添加上"use strict",只是縮進(jìn)有點(diǎn)問(wèn)題,因?yàn)闆](méi)有格式化這一步了.
復(fù)制代碼 代碼如下:

function B() {
"use strict";

alert("B")
}

SpiderMonkey最新版的jsfun.cpp源碼中有對(duì)應(yīng)的注釋

// 如果一個(gè)函數(shù)的某個(gè)上層函數(shù)中擁有"use strict",那么這個(gè)函數(shù)就繼承了上層函數(shù)的嚴(yán)格模式.
// 我們也會(huì)在這個(gè)內(nèi)部函數(shù)的函數(shù)體內(nèi)插入"use strict".
// 這就確保了,如果這個(gè)函數(shù)的toString方法的返回值被重新求值時(shí),
// 重新生成的函數(shù)會(huì)和原函數(shù)有著相同的語(yǔ)義.


而不同的是,其他瀏覽器都是不帶"use strict"的:
復(fù)制代碼 代碼如下:

function B() {
alert("B")
}

雖然這不會(huì)有什么太大影響,但我覺(jué)的Firefox的實(shí)現(xiàn)是更合理的.

相關(guān)文章

最新評(píng)論