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 tothis
. The problem is that you're callingthis.setTimeout()
instead ofwindow.setTimeout()
. – Pointy Commented Dec 2, 2017 at 14:06
3 Answers
Reset to default 5In 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