Using CoffeeScript’s Fat Arrow to Preserve `this` in Callbacks
Learn how CoffeeScript’s fat arrow (=>) automatically binds `this` in callbacks, removing the need for `.bind(this)` or `var self = this`.
03 Aug 2026, 05:38 UTC

The problem: losing context in asynchronous callbacks
When you write a class method that needs to be passed as a callback (e.g., to setTimeout or addEventListener), the function loses its original this binding in JavaScript. In CoffeeScript this often leads to bugs where @property or this.method() returns undefined.
Thesis: the fat arrow (=>)
CoffeeScript’s fat arrow function syntax captures the surrounding this value at the point of definition, compiling to a JavaScript function that preserves that context without manual .bind(this) or auxiliary variables.
How it works under the hood
Consider a simple class:
class Logger
constructor: (@prefix) ->
log: =>
console.log @prefix, 'message'
logger = new Logger('APP')
setTimeout logger.log, 200
The log method is defined with a fat arrow. When compiled to JavaScript (CoffeeScript 2.x), it becomes roughly:
var Logger = (function() {
function Logger(prefix) {
this.prefix = prefix;
}
Logger.prototype.log = function() {
var _this = this;
return function() {
return console.log(_this.prefix, 'message');
}.call(_this);
};
return Logger;
})();
var logger = new Logger('APP');
setTimeout(logger.log, 200);
The generated wrapper var _this = this; return function(){ ... }.call(_this); ensures that when the callback executes, this inside the function refers to the original Logger instance.
Worked example: event handler in a DOM component
Suppose you have a CoffeeScript class that attaches a click listener to a button:
class Button
constructor: (@element) ->
@element.addEventListener 'click', @handleClick
handleClick: =>
console.log 'Button clicked, id:', @element.id
btn = new Button(document.getElementById('save'))
Without the fat arrow, @element would be undefined inside handleClick because the listener would receive a bare function with its own this. The fat arrow guarantees the correct element reference.
Trade‑offs and limitations
- Not a constructor: Fat arrow functions lack a
prototypeproperty, sonew MyFatArrow()throwsTypeError: MyFatArrow is not a constructor. Use the thin arrow (->) or a regular function when you need instantiability. - Debugging opacity: The generated JavaScript hides the original CoffeeScript source. Enable source maps (
coffee -m file.coffee) to map breakpoints back to the CoffeeScript lines; otherwise you’ll see only the wrapped function. - Implicit return: The fat arrow returns the last expression, which can be surprising if you intend to return
undefinedexplicitly. Add an explicitreturn undefinedif needed.
Practical verification steps
- Compile the CoffeeScript file with source maps:
coffee -c -m example.coffee(requires CoffeeScript installed globally or locally; no special permissions needed). - Open the generated
example.jsand look for the patternvar _this = this; return function(){ ... }.call(_this);inside the method definition. - Run the JavaScript in a browser console or Node (
node example.js) and confirm that the logged output shows the expected instance properties (e.g., the prefix or element id). - Toggle source maps in your debugger (Chrome DevTools → Sources → enable source maps) and set a breakpoint on the CoffeeScript line; the breakpoint should hit and show the original source.
Actionable closing
If you are writing CoffeeScript classes that interact with asynchronous APIs, prefer the fat arrow for methods that will be used as callbacks. It eliminates boilerplate, reduces the chance of context‑loss bugs, and works across all 1.x and 2.x releases without needing ES6 features. Remember to pair it with source maps for debugging and reserve the thin arrow or regular functions when you need a constructor.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.