最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - setTimeout Illegal invocation TypeError: Illegal invocation - Stack Overflow

programmeradmin3浏览0评论

Illegal invocation TypeError: Illegal invocation is thrown when calling setTimeout with a variable callback. I read that this happens when this refers to another object in the callback and that bind or arrow functions are used to solve this. However, no this in my callback.

The code is as follows:

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'); 

Illegal invocation TypeError: Illegal invocation is thrown when calling setTimeout with a variable callback. I read that this happens when this refers to another object in the callback and that bind or arrow functions are used to solve this. However, no this in my callback.

The code is as follows:

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'); 

Note that I use babel and es2015.

Share Improve this question asked Dec 2, 2017 at 14:00 Jalal MostafaJalal Mostafa 1,02411 silver badges26 bronze badges 1
  • 3 The function is throwing the error because you've copied a reference to it to your own object, and then you are invoking it with your own object as the this context. It makes no difference that the callback does not refer to this. The problem is that you're calling this.setTimeout() instead of window.setTimeout(). – Pointy Commented Dec 2, 2017 at 14:06
Add a ment  | 

3 Answers 3

Reset to default 5

In the sample code, functions setTimeout and clearTimeout are being invoked with an invalid context (this). On possible fix is binding to the correct context (window):

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

It is usual to think of the this object inside a function pointing to the object to the left of the dot in the invocation:

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

When there's no dot, it depends whether the caller function is strict:

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

And

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

Given we call setTimeout without a dot, we may think that the this context doesn't matter. But in browsers it will plain if you call it with a dot, or use a variant that pass as context something different than window.

Firefox:

Uncaught TypeError: Illegal invocation

Chrome:

TypeError: 'setTimeout' called on an object that does not implement interface Window.


Others suggested sticking to the usual setTimeout(fn, timeout). Yet another way is creating an anonymous function:

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

There is no this in your callback, however setTimeout is called globally.
So this.setTimeout should be 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

I also changed your log line because your alarm does not have a name.

I got this error because I passed the wanted context to setTimeout instead of its callback

this the wrong code, where I pass this to setTimeout

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

I correct way to pass you context is to pass it to setTimeout callback

I used $.proxy to do that

here is the correct code

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

hope this helps you

发布评论

评论列表(0)

  1. 暂无评论