JavaScript:ActiveXオブジェクトとapply()関数の問題
-
07-07-2019 - |
質問
iにはActiveXオブジェクト(マスター)があり、その上で関数を動的に呼び出したい。これを行うには、apply()関数を使用します。しかし悲しいことに、InternetExplorerは「このオブジェクトはこのメソッドをサポートしていません」という線に沿って何かを教えてくれます。誰かが私にできることのヒントを教えてもらえますか?
(これをテストするには、小さな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);
}
比較のために、これは実際のapply()関数です:
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);
}
解決 4
時間と詳細な説明についてkangaxに感謝します! 悲しいことに、私はそれをこのように動作させることができませんでした(それはアラートボックスでも動作します) しかし、プロキシクラスを使用するという考えに至りました。使用したいオブジェクトのすべての関数を提供する必要があるため、最もエレガントな方法ではありませんが、動作し、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);
}
もう一度、お時間をありがとう!
他のヒント
IE(IEだけでなく)のホストオブジェクト(つまり、非ネイティブオブジェクト)の問題は、 Function.prototype
から継承しないことです(多くの場合、どちらからも継承しません)トップレベル Object.prototype
)。一部のホストオブジェクトは、関数のように見える可能性があります、呼び出せることを除いて、実際には関数とは関係ありません。これらのオブジェクトが Function.prototype
を継承しないということは、 instanceof
演算子で関数として識別されないことを意味します。コンストラクターが Function
を参照していないこと。また、 call
や apply
などの Function.prototype。*
メソッドがすべて不足していること。それらの内部[[Class]]プロパティでさえ、ネイティブ関数のように&quot; Function&quot;のプロパティではないかもしれません([[Class]]は Object.prototype.toString <の結果から推測できることに注意してください) / code> value)。
ネイティブオブジェクトが行う多くのことを実装するためにホストオブジェクトは必要ないため、これは実際に予想されます(ECMA-262、第3版)。ホストオブジェクトがメソッド呼び出しでエラーをスローすることは完全に許可されています(例: hostObject.hostMethod()
);または、 delete
などの標準演算子にオペランドとして渡す場合(例: delete hostObject.hostMethod
)。ご覧のとおり、呼び出し可能なホストオブジェクトがネイティブの Function.prototype
を継承しないようにしてもかまいません。
このような予測不可能な(まだ完全に準拠した)動作は、実際には、ホストオブジェクトの拡張が推奨されない主な理由の1つです。
ただし、 call
の問題に戻ります:)
これらの「トリッキー」に関することIEホストオブジェクトは、多くの場合、内部[[Call]]メソッドを実装し、 call
および apply
を呼び出すことができますが、直接ではありません。
これを持たないオブジェクトでの apply
呼び出しをエミュレートするパターンは次のとおりです。
function f(){ return arguments };
Function.prototype.apply.call(f, null, [1,2,3]); // [1,2,3]
もちろん、 null
は、呼び出すべきコンテキストオブジェクトに置き換えることができます。
また、 call
を持たないホストオブジェクトでの apply
呼び出しの例:
// 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"]);
これと同じ問題が発生し、実行時にサンク関数をコンパイルして適切な数の引数を展開することで解決しました(以前のソリューションと同様ですが、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);
};
IEのJSエンジンは、 apply()
を呼び出すことができるJavaScript関数オブジェクトとしてActiveX関数を認識していないようです。 eval()
を実行するのはいかがですか?-いですが、それが唯一の選択肢のようです。
function invoke(objectName, fnName, args) {
return eval(objectName + "." + fnName + "(" + args + ")");
}
Ates Goralが言ったように 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
}
so invokeは
になります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]);