2009-09-16 51 views
3

我有一個ActiveX對象(主),並想動態地調用它的功能。爲此,我使用apply()函數。但不幸的是,InternetExplorer告訴我一些大致的內容:「這個對象不支持這種方法」。有人能給我一個暗示我能做些什麼嗎?JavaScript:有ActiveX對象的問題和apply() - 函數

(爲了測試這個你也可以使用一個小的flash對象爲主機,稱之爲「DoSomething的」,而不是我的具體的「初始化」。)

function invoke(object, fnName, args) 
{ 
    return object[fnName].apply(object, args); 
} 

function test_it() 
{ 
    try{ 
    Master = window.document["Master"]; 
    } 
    catch(e){alert(e);} 
    var param = [1,"VC2"]; 
    var ret = invoke(Master, "Initialize", param); 
    alert("got: "+ret); 
} 

對於comparsion,這是適用於()函數動作:

function Obj() 
{ 
    this.msg = function(a, b, c) 
    { 
     alert("msg: \n a: "+a+"\n b: "+b+"\n c: "+c); 
     return "hi"; 
    } 
    return this; 
} 


function invoke(object, fnName, args) 
{ 
    return object[fnName].apply(object, args); 
} 

function test_it() 
{ 
    var obj = new Obj(); 
    var ret = invoke(obj, "msg", [1, 2, 3]); 
    alert("got: "+ret); 
} 

回答

1

感謝kangax爲你的時間和大量的解釋! 不幸的是我不能這樣工作(它適用於alertbox) 但它使我想到使用代理類。這不是最優雅的方式,因爲我必須從我想使用的對象中提供每個函數,但它的工作原理並不涉及eval()!

function proxy(obj) 
{ 
    this.obj = obj; 

    this.Initialize = function(a, b) 
    { 
     return obj.Initialize(a, b); 
    } 
} 

function test_it() 
{ 
    var myMaster = new proxy(window.document["Master"]);  
    var ret = myMaster["Initialize"].apply(myMaster, [1, "VC2"]); 
    alert(ret); 
} 

再次感謝您的時間!

+0

好的。很高興它有幫助:) – kangax 2009-09-17 13:08:24

+0

@hobotron我認爲,如果你嘗試用我的編輯Kangax的答案,它應該工作! – 2012-06-12 17:18:40

1

顯然IE的JS引擎不看的ActiveX功能上,您可以撥打apply() JavaScript函數對象。如何做一個eval() - 雖然很醜,但它似乎是你唯一的選擇。

function invoke(objectName, fnName, args) { 
    return eval(objectName + "." + fnName + "(" + args + ")"); 
} 
3

與一些在IE(不僅IE)宿主對象(即任何非天然的對象)的問題是,它們不繼承Function.prototype(通常既不從頂層Object.prototype)。一些主機對象可能看起來像函數實際上與函數無關,除了它們可以被調用。這些對象不從Function.prototype繼承的事實意味着它們不能被instanceof運算符識別爲函數;他們的構造函數沒有引用Function;並且他們缺少所有的Function.prototype.*方法,如callapply。即使它們的內部[[Class]]屬性可能不是「Function」的屬性,因爲它與任何本地對象一樣(請注意,可以從Object.prototype.toString value()的結果推斷出[[Class]]。

這實際上是預期的,因爲主機對象不需要來實現許多本機對象所做的事情(按照ECMA-262第3版)。主機對象完全允許在方法調用時拋出錯誤(例如hostObject.hostMethod());或者將它作爲操作數傳遞給像delete這樣的標準操作符(例如delete hostObject.hostMethod)。如您所見,可調用主機對象不會從本地Function.prototype繼承。

這種不可預知(但完全符合)的行爲實際上是爲什麼主機對象增強建議針對的主要原因之一。

但是,回到你的call問題:)

這些「棘手」 IE主機對象的事情是,他們經常實施內部[電話]的方法,它可以在其上調用callapply ,儘管不是直接

這裏有一個模式來效仿的對象,沒有它apply調用:

function f(){ return arguments }; 
Function.prototype.apply.call(f, null, [1,2,3]); // [1,2,3] 

null可以與任何上下文對象應該叫,當然可以更換。

和具有apply調用主機對象的例子沒有call

// should work in IE6, even though `alert` has no `call` there 
Function.prototype.call.call(alert, window, 'test'); 

把它應用到你的代碼

// Calls Master.initialize borrowing from Function.prototype 
Function.prototype.apply.call(Master.initialize, Master, [1,"VC2"]); 
+0

這是真正的答案。主機對象方法不支持'Function.prototype.apply',但你總是可以借用它。 – 2012-06-12 17:03:55

0

只是想我會提到,如果使用eval方法阿泰古拉爾說,你需要小心的數組中的字符串參數,因爲它們將被視爲變量名,如

function invoke(objectName, fnName, args) { 
    return eval(objectName + "." + fnName + "(" + args + ")"); 
} 
invoke("Master", "Initialize", [1, "VC1"]); 

eval將通過該行

Master.Initialize(1,VC1) 

如果VC1不是定義的變量,將會引發錯誤。這可能是最好的傳遞文字的「展開」陣列名稱改爲:

function UnrollArray(arrayname, length) { 
    var s = ""; 
    for(var i = 0; i < length; i++) { 
     s += arrayname + "[" + i + "],"; 
    } 
    return s.substring(0, s.length - 1); //remove the trailing comma 
} 

所以調用變得

function invoke(objectName, fnName, args) { 
    var unrolledarray = UnrollArray("args", args.length); 
    return eval(objectName + "." + fnName + "(" + unrolledarray + ");"); 
} 
invoke("Master", "Initialize", [1, "VC1"]); 

eval將被傳遞

Master.Initialize(args[0],args[1]); 
2

我有同樣的問題,我通過在運行時編譯一個thunk函數來解決它,以展開正確數量的參數(類似於以前的解決方案,但沒有ActiveX對象的限制句柄必須位於全局變量中)。

varArgsThunkFunctionsCache = []; 

function getVarArgsThunkFunction(arrayLength) { 
    var fn = varArgsThunkFunctionsCache[arrayLength]; 
    if (!fn) { 
    var functionCode = 'return o[m]('; 
    for (var i = 0; i < arrayLength; ++i) { 
     if (i != 0) { 
     functionCode += ',' 
     } 
     functionCode += 'a[' + i + ']'; 
    } 
    functionCode += ')'; 
    fn = new Function('o', 'm', 'a', functionCode); 
    varArgsThunkFunctionsCache[arrayLength] = fn; 
    } 
    return fn; 
}; 


function invoke(object, methodName, args) { 
    var fn = getVarArgsThunkFunction(args.length); 
    return fn(object, methodName, args); 
};