Вопрос

у меня есть 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]);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top