为什么某些函数调用在JavaScript中被称为“非法调用”?


96

例如,如果我这样做:

var q = document.querySelectorAll;

q('body');

我在Chrome中收到“非法调用”错误。我想不出为什么这样做是有原因的。首先,并非所有本机代码功能都如此。实际上,我可以这样做:

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

var x = new o();

而且一切正常。特别是在处理文档和控制台时,我发现了这个问题。有什么想法吗?




Answers:


157

这是因为您丢失了函数的“上下文”。

你打电话时:

document.querySelectorAll()

函数的上下文是document,可以通过以下方式访问this通过该方法的实现。

当您打电话时q,不再有上下文-它是“全局”window对象。

querySelectorAll尝试使用的实现,this但不再是DOM元素,而是一个Window对象。该实现尝试调用Window对象上不存在的DOM元素的某些方法,并且解释器毫不奇怪地调用foul。

要解决此问题,请.bind在较新版本的Javascript中使用:

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

这将确保对的所有后续调用都q具有正确的上下文。如果还没有.bind,请使用:

function q() {
    return document.querySelectorAll.apply(document, arguments);
}

3
哦,打个好电话。您说对了,因为我可以这样做:q.apply(document,['body']); 而且有效。
user1152187 2012年

请注意,这对于IE中的内置函数不是必需的。例如,console.log那里没有apply方法。
hugomg

@Alnitak:是的,除了IE之外,它都可以在其他地方使用,这就是为什么您应该经常正常地传递参数的原因,例如function q(x){ return document.querySelectorAll(x); }。我真的很喜欢IE浏览器对象的另一件事是,即使您尝试从它们中读取属性,它们中的一些也会引发异常,因此您需要使用if( 'funcname' in browserobject)而不是通常的功能来测试功能if(browserobject.funcname)
hugomg 2012年

很好的答案,我真的为这种现象感到困惑,这种情况与OP完全相同。
临时用户名

1
精神震撼。谢谢。
rb-

1

在我的情况下,由于将未声明的变量传递为参数而发生了非法调用。确保在传递给函数之前先声明变量。


声明变量对这种特殊情况没有意义,因为发生非法调用是因为从DOM上下文中调用了dom依赖方法,因为当您执行q = document。那一刻,该something方法会失去文档的上下文
Anshul Sahni

1

您可以这样使用:

let qsa = document.querySelectorAll;
qsa.apply(document,['body']);

0

一种更简洁的解决方案:

const q=s=>document.querySelectorAll(s);
q('body');
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.