Почему в JavaScript определенные вызовы функций называются «незаконными вызовами»?

97

Например, если я сделаю это:

var q = document.querySelectorAll;

q('body');

Я получаю сообщение об ошибке «Незаконный вызов» в Chrome. Я не могу придумать ни одной причины, по которой это необходимо. Во-первых, это не относится ко всем функциям машинного кода. На самом деле я могу это сделать:

var o = Object; // which is a native code function

var x = new o();

И все работает нормально. В частности, я обнаружил эту проблему при работе с документом и консолью. есть идеи?

user1152187
источник
Точный дубликат "Uncaught TypeError: Illegal invocation" в Chrome
Дэн Даскалеску

Ответы:

158

Это потому, что вы потеряли «контекст» функции.

Когда вы звоните:

document.querySelectorAll()

контекст функции есть documentи будет доступен thisпри реализации этого метода.

Когда вы просто вызываете q, больше нет контекста - windowвместо этого появляется «глобальный» объект.

Реализация querySelectorAllпытается использовать, thisно это уже не элемент DOM, это Windowобъект. Реализация пытается вызвать какой-то метод элемента DOM, которого нет в Windowобъекте, и интерпретатор неудивительно вызывает ошибку.

Чтобы решить эту проблему, используйте .bindв новых версиях Javascript:

var q = document.querySelectorAll.bind(document);

что гарантирует, что все последующие вызовы будут qиметь правильный контекст. Если у вас нет .bind, используйте это:

function q() {
    return document.querySelectorAll.apply(document, arguments);
}
Альнитак
источник
3
О, хороший звонок. Вы правы, потому что я умею: q.apply (document, ['body']); и это работает.
user1152187
Обратите внимание, что это не обязательно для встроенных функций в IE. Например, в console.log нет метода apply.
hugomg
@Alnitak: Да, он работает везде, кроме IE, и поэтому часто нужно просто передавать аргументы нормально, как в function q(x){ return document.querySelectorAll(x); }. Еще одна вещь, которая мне очень нравится в объектах браузера IE, - это то, что некоторые из них выдают исключение, если вы пытаетесь прочитать из них свойство, поэтому вам нужно тестировать функции с помощью if( 'funcname' in browserobject)вместо обычных if(browserobject.funcname)!
hugomg
Отличный ответ, меня очень смутило это явление, точно такая же ситуация, что и OP.
temporary_user_name
1
Разум взорван. Спасибо.
rb
1

В моем случае незаконный вызов произошел из-за передачи необъявленной переменной в качестве аргумента. Обязательно объявите переменную перед переходом к функции.

Фавад
источник
объявление переменной не имеет смысла в этом конкретном случае, поскольку происходит незаконный вызов, поскольку зависимый от dom метод вызывается из контекста DOM, потому что в тот момент, когда вы выполняете q = document.something, somethingметод теряет контекст документа
Аншул Сахни
1

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

let qsa = document.querySelectorAll;
qsa.apply(document,['body']);
любовь к кодированию
источник
0

Еще одно лаконичное решение:

const q=s=>document.querySelectorAll(s);
q('body');
BenVida
источник