setTimeout 非法调用类型错误:非法调用

Posted

技术标签:

【中文标题】setTimeout 非法调用类型错误:非法调用【英文标题】:setTimeout Illegal invocation TypeError: Illegal invocation 【发布时间】:2017-12-02 14:00:27 【问题描述】:

Illegal invocation TypeError: Illegal invocation 在使用变量回调调用 setTimeout 时抛出。我读到当this 引用回调中的另一个对象并且使用bind 或箭头函数来解决这个问题时,就会发生这种情况。但是,我的回调中没有 this

代码如下:

class AlarmService 

    constructor(callback) 
        this._alarms = ;
        this.setTimeout = window.setTimeout;
        this.clearTimeout = window.clearTimeout;
        this._callback = callback || function () ;
    

    create(alarmName, when, title, message) 
        this._alarms[alarmName] = 
            'title': title,
            'message': message
        ;
        this._alarms.timeout = this.setTimeout(this._callback, when - Date.now(),
                               this._alarms[alarmName]);
    


let alarms = new AlarmService(function (alarm) 
    console.log('Alarm', alarm.name);
);

// Exception is thrown here
alarms.create('alarmName', Date.now() + 3000, 'Title', 'Message'); 

注意我使用的是 babel 和 es2015。

【问题讨论】:

该函数抛出错误,因为您已将对它的引用复制到您自己的对象,然后您使用自己的对象作为this 上下文调用它。回调不引用this 没有区别。问题是您调用的是this.setTimeout() 而不是window.setTimeout() 【参考方案1】:

在示例代码中,函数 setTimeout 和 clearTimeout 被调用时使用了无效的上下文 (this)。可能的修复是绑定到正确的上下文 (window):

constructor(callback) 
    this._alarms = ;
    this.setTimeout = window.setTimeout.bind(window);
    this.clearTimeout = window.clearTimeout.bind(window);
    this._callback = callback || function () ;

通常认为函数内部的this对象指向调用中点左侧的对象:

alerts.create(...) // inside create(), this === alerts
   ^
   |___ "this"

当没有点时,取决于调用函数是否严格:

var create = alerts.create
create() // this === Window or global
^
|_____ no dot

'use strict'
var create = alerts.create
create() // this === undefined
^
|_____ no dot

假设我们调用setTimeout 时不带点,我们可能会认为this 上下文无关紧要。但在浏览器中,如果你用点调用它,或者使用作为上下文传递的变体不同于窗口,它会报错。

火狐:

未捕获的类型错误:非法调用

铬:

TypeError:在未实现接口 Window 的对象上调用了“setTimeout”。


其他人建议坚持通常的setTimeout(fn, timeout)。 还有一种方法是创建一个匿名函数:

this.setTimeout = (fn, timeout) => setTimeout(fn, timeout);

【讨论】:

或者使用 setTimeout 和 clearTimeout,没有这个反模式代码【参考方案2】:

您的回调中没有this,但是setTimeout 被全局调用。 所以this.setTimeout 应该是setTimeout

class AlarmService 

    constructor(callback) 
        this._alarms = ;
        this.setTimeout = window.setTimeout;
        this.clearTimeout = window.clearTimeout;
        this._callback = callback || function () ;
    

    create(alarmName, when, title, message) 
        this._alarms[alarmName] = 
            'title': title,
            'message': message
        ;
        this._alarms.timeout = setTimeout(this._callback, when - Date.now(), this._alarms[alarmName]);
    


let alarms = new AlarmService(function (alarm) 
    console.log('Alarm', alarm);
);

alarms.create('alarmName', Date.now() + 3000, 'Title', 'Message'); // Exception is thrown here
我还更改了您的日志行,因为您的警报没有名称。

【讨论】:

【参考方案3】:

我收到此错误是因为我将wanted context 传递给setTimeout 而不是its callback

这是错误的代码,我将this 传递给setTimeout

setTimeout.call(this, function () 
    // this.model.starIcon = "fa-star";
    this._toggleStarIcon()
, 150);

我传递上下文的正确方法是将其传递给setTimeout callback

我使用$.proxy 来做到这一点

这是正确的代码

setTimeout($.proxy(function () 
    // this.model.starIcon = "fa-star";
    this._toggleStarIcon()
, this), 150);

希望对你有帮助

【讨论】:

以上是关于setTimeout 非法调用类型错误:非法调用的主要内容,如果未能解决你的问题,请参考以下文章

jquery:未捕获的类型错误:非法调用

Jquery 和 HTML FormData 返回“未捕获的类型错误:非法调用”

尝试在vue js中上传图像时未捕获类型错误非法调用?

未捕获的类型错误:尝试发送包含文件的数据的 JSON 对象时非法调用

链接错误与非法调用

本地存储 setItem 覆盖 - 非法调用错误