Explain Codes LogoExplain Codes Logo

Pass correct "this" context to setTimeout callback?

javascript
callback-functions
javascript-best-practices
performance-optimization
Anton ShumikhinbyAnton Shumikhin·Dec 7, 2024
TLDR

Use an arrow function as a setTimeout callback to preserve the this context:

setTimeout(() => this.myMethod(), 1000);

Or, bind this to a traditional function:

setTimeout(this.myMethod.bind(this), 1000);

The "this" identity crisis

Dealing with the this keyword in JavaScript often feels like dealing with identity theft - the thief being setTimeout. A common gripe with setTimeout is this no longer refers to our expected context within the callback function, instead, it points to the global object. How rude!

Getting closure with closures

You can resolve this deceit by capturing, or holding hostage, the current this value (a closure) and using it within setTimeout like so:

var that = this; setTimeout(function() { that.myMethod(); // Apparently 'that' is 'this', now I'm confused again... }, 1000);

Bind vs. Theft

Looking for compatibility with older browsers, like that grandpa Internet Explorer, we can use Function.prototype.bind. This forces our function into a binding contract to uphold its this identity, even under the nefarious tricks of setTimeout:

setTimeout(this.myMethod.bind(this), 1000);

Extra parameters, extra context

HTML5 added some new specs to setTimeout allowing us to sneak in the real this as an extra parameter amongst the innocuous delay period:

setTimeout(function(a) { this.myMethod(); }, 1000, this);

However, much like rainy holidays are part and parcel of life, this method could fail in older browsers. Always check the forecast - I mean, browser compatibility.

Libraries - the hired help

Libraries like jQuery and lodash provide methods to hold this hostage ensuring it won't be impersonated during the setTimeout heist. Actions like jQuery's $.proxy() and lodash's _.bind() could be useful additions to your code base.

Dealing with a narrow Window(IE)

Speaking of older versions of IE, they don't take too kindly to outsiders like bind. In these cases, using a cloaking device (a closure, or a jQuery method) may be the only way out.

Performance hustle

There may be minor differences in performance between these methods. Although it won’t impact a setTimeout context, it's worth noting closures could cause more memory usage compared to bind or arrow functions due to that extra scope they create. It's like carrying a bit more baggage on a journey, but then, aren't we all?

Advanced Pitfalls

Implicit and Explicit binding

Oftentimes, we make mistakes by overriding the this binding provided by bind, .call(), or .apply(). Please remember, double-binding doesn't revert the this context back to its original state. That's just not its style!

Arrow functions and lexical scope

While arrow functions are leashed to their surrounding context, remember they cannot be bound using bind, call, or apply. They are more rebellious types and they love their freedom!

Compatibility with transpilers or polyfills

While using additional arguments with setTimeout in modern JavaScript, remember that not all transpilers or polyfills may support these advanced techniques. They are sort of like musical purists who insist on vinyl records. So, always check your transpiled code!