【问题标题】:setTimeout Illegal invocation TypeError: Illegal invocationsetTimeout 非法调用类型错误:非法调用
【发布时间】: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()

标签: javascript ecmascript-6


【解决方案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);
    

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 2021-07-17
      • 1970-01-01
      • 2019-04-29
      • 2017-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多