今天使用JavaScript的setTimeout遇到一个问题。如下是原代码:

setTimeout(raiseEle.setCustomValidity, 1000, ""); // raiseEle是一个HTMLInputElement

标题错误是在Chrome中抛出的。

先给出解决方案:

setTimeout(() => raiseEle.setCustomValidity(""), 1000)

看看原来的代码在各浏览器中的报错:

各浏览器报错
Safari TypeError: Con only call HTMLInputElement.setCustomValidity on instances of HTMLInputElement 
Chrome Uncaught TypeError: Illegal invocation
Edge Uncaught TypeError: Illegal invocation
Firefox Uncaught TypeError: 'setCustomValidity' called on an object that does not implement interface HTMLInputElement.
Opera Uncaught TypeError: Illegal invocation
SeaMonkey TypeError: 'setCustomValidity' called on an object that does not implement interface HTMLInputElement.

似乎没有在raiseEle上调用该方法?

我尝试过使用以下方案:

setTimeout(raiseEle.setCustomValidity.call, 1000, raiseEle, "")
各浏览器报错
Safari TypeError: Window is not a function
Chrome Uncaught TypeError: object is not a function
Edge Uncaught TypeError: object is not a function
Firefox Uncaught TypeError: Function.prototype.call called on incompatible Proxy
Opera Uncaught TypeError: object is not a function
SeaMonkey TypeError: Function.prototype.call called on incompatible Proxy

从Safari的情况来看,难道setTimeOut底层把window当做函数调用了吗?从Chrome、Edge和Opera看,似乎确实将一个对象当做函数调用了。而Firefox的信息似乎更明确,意为在不兼容的代理上调用Function.prototype.call。但没看懂什么意思。

我也尝试过这种方案:

setTimeout(raiseEle.setCustomValidity.apply, 1000, raiseEle, [""])

在各大浏览器中均有报错:

各浏览器报错
Safari TypeError: Window is not a function
Chrome Uncaught TypeError: Fonction.prototype.apply was called on #<Window>, which is a object and not a function
Edge Uncaught TypeError: Function.prototype.apply was called on #<Window>, which is a object and not a function
Firefox Uncaught TypeError: Function.prototype.call called on incompatible Proxy
Opera Uncaught TypeError: Fonction.prototype.apply was clled on #<window>, which is a object and not a function
SeaMonkey TypeError: Function.prototype.call called on incompatible Proxy

从这次结果来看,问题似乎很明显了。在确认答案之前我们不妨给出自己的见解。我们这两次传参都是将函数对象直接传给setTimeOut,并希望在回调时像我们传入那样调用:

// 尝试1
raiseEle.setCustomValidity("");
// 尝试2
raiseEle.setCustomValidity.call(raiseEle, "");
// 尝试3
raiseEle.setCustomValidity.apply(raiseEle, [""]);

但实际上传入的是函数本身,并没有绑定(bound)调用函数的对象,此函数在setTimeout中的调用也许更像这样:

// 尝试1
Global.setCustomValidity("");
// 尝试2
Global.call(raiseEle, "");
// 尝试3
Global.apply(raiseEle, [""]);

这里的Global是运行时从最开始就创建的对象,在浏览器中通常为window。我们在node.js中测试一下:

let test = {callback(){console.log("callback")}
}setTimeout(test.callback, 100)

但结果和我预想的不同,各浏览器包括node.js都能顺利找到test.callback的调用者。

难道是因为函数在prototype上的缘故吗?

function Test(){this.id = Math.random()
}Test.prototype.callback = function(){console.log("callback", this.id);
}setTimeout((new Test()).callback, 1000)
setTimeout(() => (new Test()).callback(), 2000)

这次不仅将函数定义在了对象的prototype上,还在构造函数中为实例添加了id属性。结果1秒后调用log出的id属性为undefined,而2秒后log出的id属性没问题。

以上两个示例说明定义在对象中的函数,其this已经死死绑定到改对象,而在prototype中定义的函数没有绑定this到实例,在setTimeout调用时也不会将调用者当做this传入,而是仅仅将函数体本身调用一次。

为了验证结论,我翻开了红宝书:

对象中方法的this会被解析为该对象。

这个this和调用上下文无关,无论该函数如何被调用,或者用一个指针指向该函数再调用指针,该方法也会使用正确的this。

以下代码证实结论:

let mmp = (new Test()).callback;
mmp();
let nnp = () => (new Test()).callback()
nnp();

mmp函数输出this.id值为undefined,而nnp函数输出this.id值为一个浮点数。

这样很容易解释为什么第二种和第三种方案也会报错了。因为window对象中没有call和apply函数,或者window对象不是一个Function,不能调用call和apply来调用,因此才会出现诸如window对象不是函数以及Function.prototype.call和Function.prototype.apply调用使用不正确的代理这些错误。

结论:

在setTimeout中调用函数对象不会理会函数的调用上下文,而直接单独地调用函数体本身。

Uncaught TypeError: Illegal invocation相关推荐

  1. Ajax方式上传文件报错Uncaught TypeError: Illegal invocation

    今天使用ajax上传文件时,出现了错误.数据传输的方式是通过定义formData完成的,提交的文件对象也设置为dom对象,但是还是不能发送请求.F12看到后台报了个错误:Uncaught TypeEr ...

  2. 【JavaScript】Uncaught TypeError: Illegal invocation

    通过JQuery的ajax调用post方法提交时,报了这个错误Uncaught TypeError: Illegal invocation,这是因为在提交的变量时有非法值,比如{title:$titl ...

  3. Uncaught TypeError: Illegal invocation问题解决方法

    Uncaught TypeError: Illegal invocation问题解决方法 参考文章: (1)Uncaught TypeError: Illegal invocation问题解决方法 ( ...

  4. 奇怪的报错(uncaught typeerror illegal invocation)

    奇怪的错误 uncaught typeerror illegal invocation 这个错误一般是ajax引起的,分为两种情况 1请求类型有误,即get请求,但后台实际为post请求 2即ajax ...

  5. Uncaught TypeError:Illegal invocation

    Uncaught TypeError:Illegal invocation: 中文意思是:未捕获的异常:非法调用 我是在使用ajax发送请求时出现的这个错误, 参考:https://www.cnblo ...

  6. Uncaught (in promise) TypeError: Illegal invocation

    调接口时加上: contentType:false, processData:false

  7. jquery ajax报Uncaught TypeError :Illegal invocation

    使用jquery ajax异步提交的时候报Uncaught TypeError :Illegal invocation错误,报错信息如图: 网上关于这个问题有两个解决方案: 1.请求类型有误,如,po ...

  8. ajax上传文件 获取失败,Ajax上传文件/照片时报错TypeError :Illegal invocation的解决方法...

    本篇文章给大家带来的内容是关于Ajax上传文件/照片时报错TypeError :Illegal invocation的解决方法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 问题 A ...

  9. “Uncaught TypeError: string is not a function”

    http://www.cnblogs.com/haitao-fan/archive/2013/11/08/3414678.html 今天在js中写了一个方法叫做search(),然后点击按钮的时候提示 ...

  10. chrome浏览器不能录音:Uncaught TypeError: Cannot read property ‘getUserMedia‘ of undefined解决方法

    Uncaught TypeError: Cannot read property 'getUserMedia' of undefined解决方法 javascript 打不开浏览器录音功能的问题解决方 ...

最新文章

  1. 命令别名的设置alias,unalias
  2. 从实现iPhone的OAuth封装看国内互联网和开放平台
  3. 关于Input内容改变的触发事件
  4. 屏蔽敏感词的正则表达式
  5. CGI方式获取RTX中用户的电话和邮箱
  6. php实现微信清粉功能,PHP实现微信提现功能
  7. Android之jni编译报错comparsion between signed and unsigned integer expressions解决办法
  8. 使用Tomcat部署应用
  9. java xml接口实例化_Spring简介及xml配置
  10. 编程体系结构(07):JavaEE之Web开发
  11. 苹果电风扇软件Macs Fan Control Pro安装教程
  12. WIN7使用各种激活软件都不管用的解决办法
  13. 竖着的seekbar_自定义竖着的SeekBar | 学步园
  14. excel 如何冻结多行多列
  15. 深入浅出Meta Learning - 让机器学会如何去学习
  16. 模拟信号拉线位移编码器是如何来校准的?
  17. JUnit version 3.8 or later expected
  18. 日常收入和支出怎么记账
  19. 解决控制台Could not resolve view with name ...
  20. HTTP下载文件校验失败原因分析与解决

热门文章

  1. R语言—基本统计分析
  2. Springboot 发邮件端口问题
  3. 使用Druid SQL Parser解析SQL
  4. FPGA浮点运算实战
  5. 中国慕课moocpython答案_中国大学慕课mooc2020用Python玩转数据题目答案
  6. jQuery下载和使用
  7. 零基础学前端系列教程 | 和前端谈恋爱的第004天——打扮漂亮
  8. 视频伪原创工具 剪辑过的视频md5会改变吗
  9. 【总结】NPU/CPU/GPU 傻傻分不清?
  10. 图片压缩导致IOS内存不足导致崩溃