這是一篇關于使用JScriptRuntimeObject(MSDN)調試的文章。雖然這些例子中的大多數在其他瀏覽器中不能運行,但在IE 5.5+中都能運行。
泄露的全局標識符
比如說你一不小心創建了一個全局屬性,如:
function playRugby(players) {
var items,
i;
len = items.length; // Global.
}
function kick() {
var x = 10
y = 11; // As I makes y global.
}
當playRugby被調用時,全局屬性len被創建,如果它尚未存在,那么就將items.length的值賦給它。同樣,當kick被調用時,全局屬性y被創建。
這些全局變量都不是故意的。它們破壞了封裝并泄露了執行的細節。這可能會導致沖突和棘手的依賴問題。
要檢測這些不經意間創建的全局標識符,我們可以使用for in循環全局對象。Firebug的“DOM”標簽提供了這個實用的全局檢測。
遺憾的是,在IE中,for in不能枚舉任何全局變量和函數聲明。看看下面的例子:
// Property of global variable object.
var EX1_GLOBAL_VARIABLE = 10;
// Property of global object.
this.EX1_GLOBAL_PROPERTY = 11;
// Property of global variable object.
function EX1_GLOBAL_FUNCTION(){}
(function(){
var results = [];
for(var p in this) {
results.push(p);
}
alert("Leaked:" + results.join(""));
})();// Property of global variable object.
var EX1_GLOBAL_VARIABLE = 10;
// Property of global object.
this.EX1_GLOBAL_PROPERTY = 11;
// Property of global variable object.
function EX1_GLOBAL_FUNCTION(){}
(function(){
var results = [];
for(var p in this) {
results.push(p);
}
alert(“Leaked:” + results.join(“”));
})();
在IE中,結果包含一個window屬性組合和用戶定義的四個屬性之一:EX1_GLOBAL_PROPERTY 。
那么,其它三個用戶定義的屬性發生了什么?為什么它們不能在for in循環中顯示出來。
事實證明,枚舉全局對象時將枚舉已賦值的全局對象屬性,而不枚舉全局變量。
為什么全局屬性能枚舉而全局變量不能。經驗告訴我們,JScript 給全局變量(用var聲明)打上了DontEnum標記。由于全局對象是作為全局變量對象來定義的,這看起來似乎是一個合理的解釋。這并不標準,但能解釋IE中的行為。不過,Eric Lippert 提出了另一種解釋:全局對象和全局變量對象是IE中兩個不同的對象。
根據MS-ES3:
JScript 5.x 中的變量聲明創建了全局對象的屬性,該屬性擁有DontEnum 特性。
枚舉方案:JScript RuntimeObject
使用JScript RuntimeObject方法枚舉全局屬性與枚舉全局對象不同,你將使用一個正常的執行,枚舉由全局RuntimeObject方法返回的一個對象。
var GLOBAL_VAR1,
GLOBAL_VAR2,
GLOBAL_VAR3 = 1;
GLOBAL_PROP1 = 12;
function GLOBAL_FUNCTION(){}
if(this.RuntimeObject){
void function() {
var ro = RuntimeObject(),
results = [],
prop;
for(prop in ro) {
results.push(prop);
}
alert("leaked:" + results.join(""));
}();
}var GLOBAL_VAR1,
GLOBAL_VAR2,
GLOBAL_VAR3 = 1;
GLOBAL_PROP1 = 12;
function GLOBAL_FUNCTION(){}
if(this.RuntimeObject){
void function() {
var ro = RuntimeObject(),
results = [],
prop;
for(prop in ro) {
results.push(prop);
}
alert(“leaked:” + results.join(“”));
}();
}
IE中的結果
在IE8和其它較低版本中,結果包括GLOBAL_FUNCTION,GLOBAL_VAR3,GLOBAL_PROP1 (除此之外,還包括window)。注意GLOBAL_VAR1 和GLOBAL_VAR2 并不包含其中。看來RuntimeObject并不收集未被賦值的任何變量,根據微軟的文檔,這不是指定的行為(以下有更多這方面的信息)。
微軟的RuntimeObject文檔
RuntimeObject是JScript 內置的擴展,JScript定義了七個附加的內置全局方法:ScriptEngine,ScriptEngineBuildVersion,ScriptEngineMajorVersion,ScriptEngineMinorVersion,CollectGarbage,RuntimeObject和GetObject。這些對象都是本地JScript對象,不要與宿主對象混淆。
對于RuntimeObject,微軟的JScript擴展MS-ES3EX聲明如下:
RuntimeObject用來尋找一個全局變量的屬性,這些帶有名稱的屬性匹配特定的模式。這個函數只尋找全局對象中通過VariableStatement 或 FunctionDeclaration方式顯式創建的屬性,或是位于運算符左側作為標識符隱式創建的屬性。不能尋找通過訪問全局對象顯式創建的屬性。
粗略的測試結果表明微軟的文檔是不靠譜的。
返回的對象不包括添加到變量對象之上的所有標識符,只是那些被賦值的標識符。無論它們是通過VariableDeclaration,FunctionDeclaration來創建,還是作為全局屬性聲明來創建都無關緊要。
查找通過FunctionBindingList創建的標識符示例
在JScriptFunction的FunctionBindingList 中,所有標識符將成為包含的變量對象的屬性,如:
var foo = {}, undef, ro;
(function(){ function foo.bar, baz(){} })();
ro = RuntimeObject();
alert([ro.foo.bar, "undef" in ro].join(""));var foo = {}, undef, ro;
(function(){ function foo.bar, baz(){} })();
ro = RuntimeObject();
alert([ro.foo.bar, "undef" in ro].join(“”));
IE elerts
function foo.bar(){}
false在除了IE的瀏覽器中運行JScript,在解析JScriptFunction 的FunctionBindingList時會如期地拋出SyntaxError(語法錯誤)。這是意料之中的,因為它是一個語法擴展。
書簽
javascript:(function() {var ro=RuntimeObject(),r=[],i=0,p;for(p in ro){r[i++]=p;}alert('leaked:'+r.join(''));})();JScript語法擴展
在較早的“查找由FunctionBindingList創建的標識符”的例子中提到過JScript擴展--JScriptFunction。這個名字不是一個破綻,它是JScript語言的擴展,JScriptFunction 如下:
JScriptFunction : function FunctionBindingList ( FormalParameterListopt ) { FunctionBody }
RuntimeObject(filterString): The filterString Parameter
RuntimeObject方法接受一個可選的過濾字符去匹配標識符。遺憾的是,filterString 不能轉換成正則表達式,而是用可選的leftWild和rightWild來進行子匹配默認為“*”。
這意味著filterString = “a*” 將匹配標識符a和a1,而不是ba 。
結論
拋開文檔的bug和缺點來說,RuntimeObject 提供了一個有用的替代方案來解決JScript中枚舉全局屬性的問題。RuntimeObject的優點在于它僅包括用戶定義的屬性,全局的window屬性除外。
前面提到的書簽提供了一個簡便的方法去查看一個頁面中不經意間創建的全局屬性(它也表明這個站點也不是全局對象保持清潔的光輝典范)。
RuntimeObject的其它應用
跨瀏覽器標識泄露書簽
相對于IE中標識泄露檢測,寫一個跨瀏覽器標識泄露檢測是下一個邏輯步驟。
自動標識符泄露檢測
意外的全局標識符檢測應該是自動匹配的。
YUI Test 的單元測試框架為TEST_CASE_BEGIN_EVENT 和TEST_CASE_COMPLETE_EVENT 提供了鉤子。這些事件用來檢測RuntimeObject,并在程序代碼運行期間捕捉泄露的標識符。
在TEST_CASE_BEGIN_EVENT 中檢測RuntimeObject 并存儲結果。在TEST_CASE_COMPLETE_EVENT 中再次檢測RuntimeObject 并與TEST_CASE_BEGIN_EVENT中存儲的結果比較。接下來,對于每一個出現在TEST_CASE_COMPLETE_EVENT中而并沒有出現在TEST_CASE_BEGIN_EVENT中的每屬性,一個全局標識符已被泄露,一個測試案例的警告就會記錄下來。
參考
[MS-ES3EX]: Microsoft JScript Extensions to the ECMAScript Language Specification Third Edition.
原文地址:http://dhtmlkitchen.com/?category=/Browsers/&date=2010/04/11/&entry=Detecting-Global-Pollution-with-the-JScript-RuntimeObject
轉載地址:http://www.denisdeng.com/?p=1043
上一篇 什么樣的SNS才有機會成功?