搜尋
首頁web前端js教程javascript SpiderMonkey中的函數序列化如何進行_基礎知識

在Javascript中,函數可以很容易的被序列化(字串化),也就是得到函數的源碼.但其實這個操作的內部實現(引擎實現)並不是你想像的那麼簡單.SpiderMonkey中一共使用過兩種函數序列化的技術:一種是利用反編譯器(decompiler)將函數編譯後的字節碼反編譯成源碼字串,另一種是在將函數編譯成字節碼之前就把函數源碼壓縮並且儲存下來,用到的時候再解壓縮還原.

如何進行函數序列化
在SpiderMonkey中,能將函數序列化的方法或函數有三個:Function. prototype.toString,Function.prototype.toSource,uneval.只有toString方法是標準的,也就是各引擎通用的.但是ES標準中關於Function.prototype.toString方法的規定(ES5 15.3.4.2)只有寥寥數語,也就是說,基本上沒有標準,引擎自己決定該如何實現.

函數序列化的作用
函數序列化最主要的作用應該是利用序列化生成的函數原始碼重新定義這個函數.

複製程式碼 程式碼如下:

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

a() //執行時可能會彈出"a"

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

a() //執行時可能會彈出"b"

你也許會想:"我寫了這麼多年Javascript,怎麼沒有遇到這種需求".的確,如果是自己的網站,自己完全控制的js文件,不需要以這種打補丁的方式來修改函數,直接修改就可以了.但是如果源文件不是你能控制的了的話,就很有可能要這樣做了.比如常用的地方有greasemonkey腳本:你可能需要停用或修改某個網站中的某個函數.還有就是Firefox擴充:你需要修改Firefox自身的某個函數(可以說Firefox是用JS寫的).舉個我自己寫的 Firefox腳本的範例:
複製程式碼 程式碼如下:
location == "🎜> 程式碼如下:

location == " /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}) ;"))

這個代碼的作用是:在地址列上回車時,讓Firefox在新標籤中打開頁面,而不是佔用當前標籤.實現方式就是用toString方法讀取到gURLBar. handleCommand函數的原始碼,然後用正規替換後傳給eval,重新定義了這個函數.

為什麼不用直接定義的方式,也就是直接重寫函數呢:

gURLBar.handleCommand = function(){...//將原本的函數更改了一個小地方}
不能這麼做的原因是因為我們得考慮兼容性,我們應該盡可能小的更改這個函數的源碼.如果這麼寫的話,Firefox的gURLBar.handleCommand源碼一旦發生變化,這個腳本就失效了.比如Firefox3和Firefox4中都有這個函數,但函數內容差別非常大,可是如果用正則替換部分關鍵字的話,只要這個被替換的這個關鍵字沒有改變的話,就不會出現不相容的現象.
反編譯字節碼

在SpiderMonkey中,函數在被解析之後會被編譯成字節碼(bytecode),也就是說,內存中存儲著並不是原始的函數源碼.SpiderMonkey中存在一個反編譯器,它的主要作用就是把函數的字節碼反編譯成函數源碼的形式.
在Firefox16以及之前的版本中,SpiderMonkey使用的就是這種方法,如果你使用的是這些版本的Firefox的話,可以嘗試下面的代碼: 複製程式碼
程式碼如下:


alert(function () {
"字串";
//註解
return 1 2 3
}.toString())
傳回的字串是

function () {
return 6;
}
輸出和其他的瀏覽器完全不同:

1.沒有意義的原始值字面量在編譯的時候會被刪除,這個例子中就是"字符串".

你也許會覺得:"似乎沒什麼問題,反正這些值對於函數的運行來說並沒有什麼意義".等等,你是不是忘了個東西,表示嚴格模式的字串"use strict"怎麼辦呢?

在不支援嚴格模式的版本中,比如Firefox3.6,這個"use strict"和其他字串沒什麼區別,編譯的時候會被刪除.在SpiderMonkey實現了嚴格模式之後,雖然編譯的時候同樣會忽略掉這個字符串"use strict",但在反編譯的時候會進行判斷,如果這個函數處於嚴格模式中,則會在函數體的第一行新增上"use strict",下面是對應的引擎原始碼.

static JSBool
複製程式碼



複製程式碼



複製程式碼



程式碼如下:


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(>js_printf(>js_printf(>js_printf( jp, "t"use strict";n");
}
jp->strict = true;
2.註解在編譯的時候也會刪除


這個貌似沒太大影響,不過有些人願意利用函數註解來實現多行字串,這個方法在Firefox 17之前的版本中是不可用的.




複製程式碼

程式碼如下:


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


*/});
console.log(string)

3.原始值編譯量的運算會在時進行. 這算是一種最佳化方式,《高效能JavaScript》提到:

反編譯的弊端

由於新技術的出現(例如嚴格模式)以及在修改其他相關bug的時候,反編譯器這部分的實現經常需要更改,更改就有可能產生新的bug,我自己就親身遇到過一個bug.大概是在Firefox10左右的時候,具體問題記不大清了,反正是關於反編譯時小括號是否要保留的問題,大概是這樣的:



複製程式碼
程式碼如下: >(function (a,b,c){return (a b) c}).toString() "function (a , b, c) {
return a b c;
}"


在反編譯時,(a b)中的小括號被省略了,由於加法結合律從左到右,所以這沒關係.但我遇到的bug是這樣的:

複製程式碼 程式碼如下: >(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 ".

關於反編譯器,Mozilla工程師Luke Wagner指出,反編譯器對他們實現一些新功能的阻礙很大,而且經常會出現一些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 this the starter fuzz bugs.The sooner we remove this this the starter we 也, 零, this. 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. 存儲函數源碼
從Firefox17之後,SpiderMonkey改成了第二種實現方法,其他瀏覽器也應該是這樣實現的吧.函數序列化得到的字串完全和源碼一致,包括空白符,註解等等.這樣的話,大部分問題就應該沒有了吧.不過,貌似我又想到個問題.還是關於嚴格模式的.

如:


複製程式碼 程式碼如下:
(function A() {


(function A() {


(function A() {


(function A() {
當然,傳回的來源碼中也應該有"use strict",所有瀏覽器都是這麼實現的:




複製程式碼


程式碼如下:
function A() { function A() { "use strict";
alert("A");
}


但如果是這樣呢:



複製程式碼


程式碼如下:


(function A() {
"use strict"; return function B() { alert(>alert(>alert( "B") } })() ""
內部函數B也處於嚴格模式中,輸出B的函數源碼應不應該加上"use strict"呢.試驗一下:

上面說了,Firefox17之前Firefox4之後的版本是通過判斷當前函數是否處於嚴格模式來決定輸出不輸出"use strict"的,函數B繼承了函數A的嚴格模式,所以會有"use strict".

同時函數源碼是縮進嚴格的,因為在反編譯的時候,SpiderMonkey會給反編譯出的源碼進行格式化,即使之前的源碼完全沒有縮進也沒關係:



複製程式碼
程式碼如下: function B() {
function B() {
" use strict";
alert("B");
}


Firefox17之後的版本會不會帶有"use strict"呢?因為是直接把函數源碼保存下來的,而且函數B中的確沒有"use strict"字樣.試驗結果是:會添加上"use strict",只是縮進有點問題,因為沒有格式化這一步了.




複製程式碼


程式碼如下:


function B() {
"use strict"; alert(" B") } SpiderMonkey最新版的jsfun.cpp原始碼中有對應的註解
// 如果一個函數的某個上層函數中擁有"use strict ",那麼這個函數就繼承了上層函數的嚴格模式.
// 我們也會在這個內部函數的函數體內插入"use strict".
// 這就確保了,如果這個函數的toString方法的回傳值被重新求值時,
// 重新產生的函數會和原函數有著相同的語意.

而不同的是,其他瀏覽器都是不帶" use strict"的: 複製程式碼 程式碼如下: function B() >alert("B") } 雖然這不會有什麼太大影響,但我覺的Firefox的實現是更合理的.
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

神秘的JavaScript:它的作用以及為什麼重要神秘的JavaScript:它的作用以及為什麼重要Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python還是JavaScript更好?Python還是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

如何安裝JavaScript?如何安裝JavaScript?Apr 05, 2025 am 12:16 AM

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

在Quartz中如何在任務開始前發送通知?在Quartz中如何在任務開始前發送通知?Apr 04, 2025 pm 09:24 PM

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?Apr 04, 2025 pm 09:21 PM

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

微信小程序webview中Vue.js動態style位移失效是什麼原因?微信小程序webview中Vue.js動態style位移失效是什麼原因?Apr 04, 2025 pm 09:18 PM

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具