Uncaught typeerror illegal invocation что за ошибка

от admin

Uncaught TypeError: Illegal invocation

Воспровожу пример из мануала Фланагана по JavaScript и получаю ошибку в Яндекс.Браузере (читай: Хроме):

В Safari тоже ошибка:

Однако в Firefox ровно этот же код работает. Что это за ошибка и как поправить код?

Ответы (3 шт):

Ну так попробуйте

Функция console.log хрома обращает внимание на контекст this , пример:

Соответственно получим ошибку Illegal invocation . Чтобы этого не происходило, вызовем эту функцию в нужном нам контексте console :

Как уже говорилось тут и там.

Функция console.log обращает внимание на контекст this, пример:

Соответственно получим ошибку Illegal invocation . Чтобы этого не происходило, вызовем эту функцию в нужном нам контексте:

Вернемся к вопросу автора: в данном случае, так как, мы принимаем произвольную функцию, нельзя определить контекст внутри метода foreach . Но, можно привязать контекст к функци и передать уже связанную функцию так:

"Illegal invocation" errors in JavaScript

The error is thrown when calling a function whose this keyword isn’t referring to the object where it originally did, i.e. when the "context" of the function is lost.

Table of contents

Example problems

I encountered the "illegal invocation" error when calling the destructured abort method of an AbortController :

Another case: trying to implement jQuery-like shorthands for document.querySelector and document.querySelectorAll :

(By the way: most, if not all, modern browsers have the $ and $$ shorthands built into the browser’s JS console.)

Description of the error

"Invocation" is the act invoking a function, which is the same as calling a function. Invoke = call.

An "illegal invocation" error is thrown when calling a function whose this keyword doesn’t refer to the object where it originally did. In other words, the original "context" of the function is lost.

Chromium browsers call this error an "illegal invocation."

Firefox produces more descriptive error messages:

TypeError: ‘abort’ called on an object that does not implement interface AbortController.

TypeError: ‘querySelector’ called on an object that does not implement interface Document.

TypeError: Can only call AbortController.abort on instances of AbortController

TypeError: Can only call Document.querySelector on instances of Document

Node.js (v16) produces clearly the best error messages, e.g.:

TypeError [ERR_INVALID_THIS]: Value of "this" must be of type AbortController

Deno uses V8 – the same JS engine as Chromium browsers do – so Deno also calls the error an "illegal invocation."

Manual implementation of an "illegal invocation" check

For demonstration purposes:

  • In real code, you should use better error messages. "Illegal invocation" is not clear.
  • In strict mode, this would be undefined instead of window in the error cases.

Why does the this keyword change?

The gist is in the difference between method invocations and function invocations.

Method invocations

A method is a function stored as a property of an object. When invoking (i.e. calling) a method using the dot notation or square bracket notation, the this keyword is bound to the object:

Function invocations

When invoking (i.e. calling) a function that is not the property of an object, the this keyword is:

  • bound to the global object ( window ) in sloppy mode.
  • undefined in strict mode.

In either mode, the original context is lost because the this keyword doesn’t refer to the object where it originally did:

As to why the context is lost – let’s quote Douglas Crockford’s book JavaScript: The Good Parts (1st ed., p. 28; emphasis added):

When a function is not the property of an object, then it is invoked as a function:

When a function is invoked with this pattern, this is bound to the global object. This was a mistake in the design of the language.

Sidetrack: arrow functions

The quote from the book continues (pp. 28–29; text split into paragraphs and code block slightly edited):

Had the language been designed correctly, when the inner function is invoked, this would still be bound to the this variable of the outer function.

A consequence of this error is that a method cannot employ an inner function to help it do its work because the inner function does not share the method’s access to the object as its this is bound to the wrong value.

Fortunately, there is an easy workaround. If the method defines a variable and assigns it the value of this , the inner function will have access to this through that variable. By convention, the name of that variable is that :

Nowadays you can alternatively use arrow functions:

Arrow functions increase the complexity around the this keyword; or reduce complexity, depending on the viewpoint.

Anyhow, the this keyword in JavaScript is confusing. I personally try to avoid it. It has many potential pitfalls, and often there are better alternatives.

Three ways to fix the error

Here’s the original, problematic example code:

The gist of the problem is that calling abort , $ or $$ is a function invocation, not a method invocation, so the context is lost.

Create a function that calls a method

As we learned above, with a method invocation (as opposed to a function invocation), the this keyword is bound to the object.

So, create an abort function that calls the abortController.abort method:

Calling abort is a function invocation, but abort in turn calls abortController.abort using method invocation, so the context is not lost.

Similarly for $ and $$ :

(By the way: notice how the function parameters are in the plural form: selectors instead of selector . That’s because document.querySelector and document.querySelectorAll accept a comma-separated list of CSS selectors.)

Use bind() to change the this keyword

A more convoluted solution is to use Function.prototype.bind() to set the this keyword to point to the correct object:

There’s also Function.prototype.apply() and Function.prototype.call() , but they are also convoluted because they deal with the this keyword.

I recommend the previous solution which doesn’t deal with the this parameter: Create a function that calls a method.

Export the whole object

(Maybe an obvious solution, but mentioning it anyway.)

In the AbortController case, I originally destructured the abort method because I wanted to export only that method, not the whole AbortController .

If you are fine with exporting the whole AbortController , calling its abort method directly is fine too (because it’ll be a method invocation):

This solution doesn’t apply to the $ and $$ functions because document is anyway available in all modules.

Sources / Further resources

I learned about "illegal invocation" errors via these Stack Overflow questions:

I learned about the differences between method invocations and function invocations from Douglas Crockford’s book JavaScript: The Good Parts. Chapter 4, "Functions," has more details, and also describes two other invocation patterns in JavaScript:

"Uncaught TypeError: Illegal invocation" in Chrome

When I use requestAnimationFrame to do some native supported animation with below code:

Directly calling the support.animationFrame will give.

Uncaught TypeError: Illegal invocation

4 Answers 4

In your code you are assigning a native method to a property of custom object. When you call support.animationFrame(function () <>) , it is executed in the context of current object (ie support). For the native requestAnimationFrame function to work properly, it must be executed in the context of window .

Читать:
На какое наибольшее число можно разделить

So the correct usage here is support.animationFrame.call(window, function() <>); .

The same happens with alert too:

Another option is to use Function.prototype.bind() which is part of ES5 standard and available in all modern browsers.

You can also use:

Michał Perłakowski's user avatar

When you execute a method (i.e. function assigned to an object), inside it you can use this variable to refer to this object, for example:

If you assign a method from one object to another, its this variable refers to the new object, for example:

The same thing happens when you assign requestAnimationFrame method of window to another object. Native functions, such as this, has build-in protection from executing it in other context.

There is a Function.prototype.call() function, which allows you to call a function in another context. You just have to pass it (the object which will be used as context) as a first parameter to this method. For example alert.call(<>) gives TypeError: Illegal invocation . However, alert.call(window) works fine, because now alert is executed in its original scope.

If you use .call() with your object like that:

it works fine, because requestAnimationFrame is executed in scope of window instead of your object.

However, using .call() every time you want to call this method, isn’t very elegant solution. Instead, you can use Function.prototype.bind() . It has similar effect to .call() , but instead of calling the function, it creates a new function which will always be called in specified context. For example:

The only downside of Function.prototype.bind() is that it’s a part of ECMAScript 5, which is not supported in IE <= 8. Fortunately, there is a polyfill on MDN.

As you probably already figured out, you can use .bind() to always execute requestAnimationFrame in context of window . Your code could look like this:

Then you can simply use support.animationFrame(function() <>); .

Michał Perłakowski's user avatar

This is a Quite Common Issue related to binding in JavaScript, and how this works —

Suppose I have an function like this document.write , if I call it directly, like following, write function is aware of this which is referring of document object, But in JavaScript there is a things called Implicity Loss, or at least thats how we refer it to

If I make a alias of the function (its is not copy ), document.element to a variable like x , you might expect that the function will keep working as it was working, right ?

No, the function x is just an alias of write function which is a native function in most browsers, which means its implemented by the browser. x is not aware of document object, so it takes whatever the global scope this at time of invoation is to be the this for the function.

So if you call function x it will give you illigal Invocation error, and to be able to use it you have make x aware of this , this is what’s called explicit binding and you can use it using call or bind like this

«Uncaught TypeError: незаконный вызов» в Chrome

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

Прямо позвонив в support.animationFrame дам.

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

в Chrome. Почему?

задан 13 марта ’12, 03:03

3 ответы

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

Итак, правильное использование здесь support.animationFrame.call(window, function() <>); .

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

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

Начиная с Chrome 33, второй вызов также завершается с ошибкой «Незаконный вызов». С радостью сниму отрицательный голос, как только будет получен ответ. обновление! — Дэн Даскалеску

@DanDascalescu: я использую Chrome 33, и он у меня работает. — Немой

Я только что скопировал ваш код и получил ошибку Illegal invocation. Вот скринкаст. — Дэн Даскалеску

Вы обязательно получите ошибку незаконного вызова, потому что первое заикание myObj.myAlert(‘this is an alert’); незаконно. Правильное использование myObj.myAlert.call(window, ‘this is an alert’) . Пожалуйста, прочтите ответы как следует и постарайтесь понять их. — Немой

Если я не единственный, кто застрял здесь, пытаясь заставить console.log.apply работать таким же образом, «this» должна быть консолью, а не окном: stackoverflow.com/questions/8159233/… — Алекс

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

Это не полностью отвечает на вопрос. Я думаю, это должен быть скорее комментарий, а не ответ. — Михал Перлаковски

Также важно выполнить привязку к соответствующему объекту, например, при работе с history.replaceState следует использовать: var realReplaceState = history.replaceState.bind(history); — ДиЮ

@DeeY: спасибо, что ответили на мой вопрос! Для будущих людей localStorage.clear требует, чтобы вы .bind(localStorage) , Не .bind(window) . — Самйок Непал

Итак, в старые добрые времена это было let log = console.log и let create = document.createElement , и теперь это let log = console.log.bind(console) и let create = document.createElement.bind(document) . Хорошо, хорошо, хорошо. — Нильс Линдеманн

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

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

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

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

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

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

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

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

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

Похожие статьи