«Uncaught TypeError: незаконный вызов» в Chrome - PullRequest
125 голосов
/ 13 марта 2012

Когда я использую requestAnimationFrame для создания некоторой встроенной поддерживаемой анимации с кодом ниже:

var support = {
    animationFrame: window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame
};

support.animationFrame(function() {}); //error

support.animationFrame.call(window, function() {}); //right

Непосредственный вызов support.animationFrame даст ...

Uncaught TypeError: Незаконный вызов

в Chrome.Почему?

Ответы [ 3 ]

179 голосов
/ 13 марта 2012

В своем коде вы присваиваете собственный метод свойству пользовательского объекта.Когда вы вызываете support.animationFrame(function () {}), оно выполняется в контексте текущего объекта (т.е. поддержки).Чтобы собственная функция requestAnimationFrame работала должным образом, она должна выполняться в контексте window.

Так что правильное использование здесь support.animationFrame.call(window, function() {});.

То же самое происходит и с предупреждением:

var myObj = {
  myAlert : alert //copying native alert to an object
};

myObj.myAlert('this is an alert'); //is illegal
myObj.myAlert.call(window, 'this is an alert'); // executing in context of window 

Другой вариант - использовать Function.prototype.bind () , который является частью стандарта ES5 и доступен во всех современных браузерах.

var _raf = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame;

var support = {
   animationFrame: _raf ? _raf.bind(window) : null
};
16 голосов
/ 04 мая 2015

Вы также можете использовать:

var obj = {
    alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
10 голосов
/ 20 января 2016

Когда вы выполняете метод (то есть функцию, назначенную объекту), внутри него вы можете использовать переменную this для ссылки на этот объект, например:

var obj = {
  someProperty: true,
  someMethod: function() {
    console.log(this.someProperty);
  }
};
obj.someMethod(); // logs true

Если вы присваиваете метод от одного объекта другому, его переменная this ссылается на новый объект, например:

var obj = {
  someProperty: true,
  someMethod: function() {
    console.log(this.someProperty);
  }
};

var anotherObj = {
  someProperty: false,
  someMethod: obj.someMethod
};

anotherObj.someMethod(); // logs false

То же самое происходит, когда вы присваиваете requestAnimationFrame метод window другому объекту. Встроенные функции, такие как эта, имеют встроенную защиту от выполнения их в другом контексте.

Существует функция Function.prototype.call(), которая позволяет вам вызывать функцию в другом контексте. Вы просто должны передать его (объект, который будет использоваться в качестве контекста) в качестве первого параметра этому методу. Например, alert.call({}) дает TypeError: Illegal invocation. Тем не менее, alert.call(window) работает нормально, потому что теперь alert выполняется в своем первоначальном объеме.

Если вы используете .call() с таким объектом:

support.animationFrame.call(window, function() {});

работает нормально, потому что requestAnimationFrame выполняется в области window вместо вашего объекта.

Однако использование .call() каждый раз, когда вы хотите вызвать этот метод, не очень элегантное решение. Вместо этого вы можете использовать Function.prototype.bind(). Он имеет эффект, аналогичный .call(), но вместо вызова функции он создает новую функцию, которая всегда будет вызываться в указанном контексте. Например:

window.someProperty = true;
var obj = {
  someProperty: false,
  someMethod: function() {
    console.log(this.someProperty);
  }
};

var someMethodInWindowContext = obj.someMethod.bind(window);
someMethodInWindowContext(); // logs true

Единственным недостатком Function.prototype.bind() является то, что он является частью ECMAScript 5, который не поддерживается в IE <= 8 </a>. К счастью, на MDN .

есть полифилл .

Как вы, наверное, уже поняли, вы можете использовать .bind(), чтобы всегда выполнять requestAnimationFrame в контексте window. Ваш код может выглядеть так:

var support = {
    animationFrame: (window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame).bind(window)
};

Тогда вы можете просто использовать support.animationFrame(function() {});.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...