Решение ошибки Illegal invocation в Chrome: animationFrame
Быстрый ответ
Ошибка "Uncaught TypeError: Illegal invocation" обычно появляется, когда метод отделяется от своего родительского объекта, как в случае с console.log
, и таким образом лишается контекста. Чтобы решить эту проблему, вы можете использовать функцию JavaScript .bind()
для привязки метода к исходному объекту:
// Некорректно! Контекст потерян, 'console' его не учтёт
var log = console.log;
log('сообщение');
// Чтобы это работало, 'console' должен быть связан с объектом
var log = console.log.bind(console);
log('сообщение');
Убедитесь что методы всегда связаны с их родными объектами через bind()
.
Тонкости использования контекста и 'this'
Загадочный "this" в JavaScript ссылается на контекст выполнения кода. Если методы, которым для работы необходим конкретный контекст, отделяются от своих объектов, то они начинают вести себя некорректно. Обычно они пытаются найти "this" внутри того объекта, к которому изначально принадлежали, но при отдельном вызове "this" указывает на глобальный объект или в строгом режиме ('strict mode') на undefined
.
Причины ошибки
Ситуации, в которых возникает "Uncaught TypeError: Illegal invocation":
- Методы DOM API, такие как
addEventListener
илиsetTimeout
, ожидают работать в контексте DOM-элемента или объекта window. - Методы консоли, наподобие
console.log
, предназначены для взаимодействия с объектом консоли. - HTML5 Web API, включая
localStorage
илиsessionStorage
, требуют привязки к объекту хранения данных.
Способы решения проблемы
- Возвращаетесь к проверенному методу .bind(), который позволяет надёжно связать
this
с нужным объектом. - Используйте .call() или .apply() для того чтобы во время вызова функции установить правильное значение
this
.
Callback-функции и обработчики событий: проблемы с контекстом
Ситуации с "Illegal invocation" часто возникают при работе с callback-функциями и обработчиками событий, особенно когда методы передаются отдельно от своих объектов.
Проблемы с callback-функциями
// Неккоректно! `this` ожидает myElement
myElement.addEventListener('click', myObj.handleClick);
// Проблема решена! `this` связан с myElement
myElement.addEventListener('click', myObj.handleClick.bind(myObj));
Проблемы обработчиков событий
Пример работы с requestAnimationFrame
, где необходимо привязать функцию к контексту window
, чтобы не возникла ошибка:
// Возможна ошибка "Illegal invocation"
window.requestAnimationFrame(myObj.animationFrame);
// Ошибки не будет! Контекст `window` определён
window.requestAnimationFrame(myObj.animationFrame.bind(window));
Визуализация
Ошибка "Uncaught TypeError: Illegal invocation" напоминает ситуацию, когда функция работает в ненужном контексте. Визуализация помогает это лучше понять:
Функция (🔧) предназначена для работы в определённом контексте (🧰), который для неё родной.
Когда она находится вне своего контекста:
🔧❌🌳 – Функция не может функционировать под деревом (🌳), так как это для неё непривычная среда.
В итоге получается:
Chrome: 🚨 "Uncaught TypeError: Illegal invocation"
Функция (🔧) должна быть внутри своей среды:
🔧✅🧰 – Функция возвращается внутрь инструмента (🧰) и работает, как ожидалось.
Совет: Всегда проверяйте, что функции работают в той среде, для которой они предназначены.
Контекст встроенных функций
Встроенные методы, такие как alert
или localStorage.setItem
, требуют правильной привязки контекста.
// Ошибка! Это вызовет ошибку
var storageSet = localStorage.setItem;
storageSet('ключ', 'значение');
// Верно! Контекст localStorage сохранён
var storageSet = localStorage.setItem.bind(localStorage);
storageSet('ключ', 'значение');
Совместимость в разных браузерах
Обработка контекста может варьироваться в зависимости от браузера. Метод Function.prototype.bind()
может потребовать полифил для старых версий Internet Explorer (IE <= 8):
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
// реализация полифила
};
}
Синдром "неявно потерянный контекст"
Этот синдром подразумевает ситуации, когда метод присваивается переменной или передаётся как callback, что приводит к типичным ошибкам Chrome, связанным с потерей контекста.
Полезные материалы
- "this" в JavaScript (MDN) – подробный анализ контекста "this".
- Привязка функций (javascript.info) – глубокое погружение в привязку "this" и других аргументов к функциям.
- Function.prototype.bind() (MDN) – метод
.bind()
. - Спецификация ECMA-262 6th Edition – об стрелочных функциях и их особенностях.
- Доступ к "this" внутри callback (Stack Overflow) – обсуждение различных подходов к работе с контекстом "this".
- Распространённые ошибки JavaScript и их решения (David Walsh) – обзор методов решения типичных ошибок в JavaScript, включая "Illegal Invocation".