JavaScript:Проблема с ActiveX-объектом и apply()-функцией
-
07-07-2019 - |
Вопрос
у меня есть ActiveX-объект (Master), и я хотел бы динамически вызывать функции на нем.Для этого я использую функцию apply().Но, к сожалению, InternetExplorer сообщает мне что-то вроде:"Этот объект не поддерживает этот метод".Кто-нибудь может дать мне подсказку, что я мог бы сделать?
(Чтобы проверить это, вы также могли бы использовать небольшой флэш-объект в качестве главного и вызвать "doSomething" вместо моего конкретного "Initialize".)
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
;и что им не хватает всех Function.prototype.*
методы, такие как call
или apply
.Даже их внутреннее свойство [[Class]] может не совпадать со свойством "Функции", как это происходит с любым собственным объектом (обратите внимание, что [[Class]] может быть выведено из результата Object.prototype.toString
значение).
На самом деле это ожидаемо, поскольку объекты хоста не требуются реализовать многие вещи, которые делают собственные объекты (согласно ECMA-262, 3-е изд.).Вполне допустимо, чтобы хост-объект, скажем, выдавал ошибку при вызове метода (например hostObject.hostMethod()
);или при передаче его в качестве операнда стандартным операторам, таким как delete
(например, delete hostObject.hostMethod
).Как вы можете видеть, также нормально, чтобы вызываемые хост-объекты НЕ наследовались от собственных Function.prototype
.
Такое непредсказуемое (но при этом абсолютно послушное) поведение на самом деле является одной из главных причин, почему рекомендуется увеличить количество объектов хоста против.
Но вернемся к вашему call
проблема :)
Особенность этих "хитрых" объектов хоста IE заключается в том, что они часто реализуют внутренний метод [[Call]], и его можно вызвать call
и apply
на них, хотя не напрямую.
Вот шаблон для подражания 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"]);
У меня была такая же проблема, и я решил ее, скомпилировав функцию 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);
};
Очевидно, JS-движок IE не видит функции ActiveX как объекты функций JavaScript, для которых вы можете вызвать apply ()
. Как насчет того, чтобы просто выполнить eval ()
- хотя это и выглядит некрасиво, но это единственный вариант.
function invoke(objectName, fnName, args) {
return eval(objectName + "." + fnName + "(" + args + ")");
}
Просто подумал, что упомяну, что если вы используете метод 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
}
так что 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]);