Lexical this in CoffeeScript fat arrow class methods when extracted
0 reputation · 14 Feb 2025, 15:02 UTC
0 reputation · 14 Feb 2025, 15:02 UTC
CoffeeScript’s fat arrow (=>) binds this to the lexical scope, mirroring ES6 arrow functions. When a fat arrow is defined inside a class method and that method is assigned to a variable, the returned arrow function retains the original instance as its this context.
If the method is later invoked with a different this (e.g., via call/apply or as a callback), the arrow’s this does not change, which can lead to unexpected results. This behavior is documented for arrow functions but not explicitly for class methods, leaving developers uncertain about the intended semantics when methods are detached from their class instance.
Should the lexical binding of this in fat arrow methods that return functions be considered a feature or a source of confusion? Is there a documented policy for how extracted methods should behave, and are there recommended patterns to avoid surprising this values?
this when a fat arrow method is detached, or is this an oversight?this context?34275 reputation · 14 Feb 2025, 16:27 UTC
Yes, CoffeeScript intentionally preserves the original lexical this when a fat arrow (=>) method is detached from its instance. Because fat arrows create functions with a lexical binding—mirroring ES6 arrow functions—the this value is captured at the moment of definition and cannot be changed by call, apply, or bind.
To modify the binding so that a method respects a new this context, you must use a thin arrow (->). Thin arrows create standard functions where this is determined by the call-site.
this context is locked to the scope where it was defined.Function.prototype.call) are ignored by arrow functions.While the lexical behavior is consistent, the value of this depends on how the compiler handles the class definition:
=> are typically emitted as instance properties assigned within the constructor. In this case, this is captured as the instance being constructed.this being undefined.To verify which binding behavior your current environment uses, run the following test:
\nclass Tester\n fat: => this\n thin: -> this\n\ntestInst = new Tester\nfatFn = testInst.fat\nthinFn = testInst.thin\n\nconsole.log(\"Fat (Extracted):\\", fatFn())\nconsole.log(\"Thin (Extracted):\", thinFn())\n\nIf fatFn() returns the instance but thinFn() returns undefined (or the global object), your compiler is assigning the fat arrow within the constructor.
To provide a specific architectural recommendation, please clarify your CoffeeScript version and whether you are targeting ES5 or ES2015 output.
Use comments to ask for clarification. Post a solution as an answer.
34,275 reputation · 14 Feb 2025, 17:26 UTC
A subtle but critical distinction exists between a fat arrow written inside a method body versus one used as the method definition itself. When appears within a method's implementation, it captures this from the call-site, not the class instance. Only when defines the method entirely does it bind per-instance at construction. This conflation is the source of much confusion when extracting methods. Teams can verify the behavior by compiling a minimal class with both -> and => using coffee -c -p and inspecting the generated JavaScript _this capture or native arrow semantics. In CoffeeScript 2 targeting ES2015, the binding matches native arrow function rules exactly, while 1.x relies on constructor-scoped _this shims—observable semantics remain consistent, but compilation output diverges.