When building a class in CoffeeScript, should all the instance method be defined using the => (\"fat arrow\") operator and all the static methods being defin
A point not mentioned in other answers that is important to note is that binding functions with fat arrow when it is not necessary can lead to unintended results such as in this example with a class we'll just call DummyClass.
class DummyClass
constructor : () ->
some_function : () ->
return "some_function"
other_function : () =>
return "other_function"
dummy = new DummyClass()
dummy.some_function() == "some_function" # true
dummy.other_function() == "other_function" # true
In this case the functions do exactly what one might expect and there seems to be no loss to using fat arrow, but what happens when we modify the DummyClass prototype after it's already been defined (e.g. changing some alert or changing the output of a log):
DummyClass::some_function = ->
return "some_new_function"
DummyClass::other_function = ->
return "other_new_function"
dummy.some_function() == "some_new_function" # true
dummy.other_function() == "other_new_function" # false
dummy.other_function() == "other_function" # true
As we can see overriding our previously defined function of the prototype causes some_function to be correctly overwritten but other_function remains the same on instances as fat arrow has caused other_function from the class to be bound to all instances so instances won't refer back to their class to find a function
DummyClass::other_function = =>
return "new_other_new_function"
dummy.other_function() == "new_other_new_function" # false
second_dummy = new DummyClass()
second_dummy.other_function() == "new_other_new_function" # true
Even fat arrow won't work as fat arrow only causes the function to be bound to new instances (which do gain the new functions as one would expect).
However this leads to some problems, what if we need a function (e.g. in the case of switching a logging function to an output box or something) that will work on all existing instances (including event handlers) [as such we can't use fat arrows in original definition] but we still need access to internal attributes in an event handler [the exact reason we used fat arrows not thin arrows].
Well the simplest way to accomplish this is to merely include two functions in the original class definition, one defined with a thin arrow which does the operations that you wish to execute, and another defined with a fat arrow that does nothing but call the first function for example:
class SomeClass
constructor : () ->
@data = 0
_do_something : () ->
return @data
do_something : () =>
@_do_something()
something = new SomeClass()
something.do_something() == 0 # true
event_handler = something.do_something
event_handler() == 0 # true
SomeClass::_do_something = -> return @data + 1
something.do_something() == 1 # true
event_handler() == 1 # true
So when to use thin/fat arrows can be summed up fairly easy in four ways:
Thin arrow alone functions should be used when the both conditions are mett:
Fat arrow alone functions should be used when the following condition is met:
Fat arrow function which directly calls a thin arrow function should be used when the following conditions are met:
Thin arrow function which directly calls a fat arrow (not demonstrated) function should be used when the following conditions are met:
In all approaches it must be considered in the case where the prototype functions might be changed whether or not behaviour for specific instances will behave correctly for example although a function is defined with a fat arrow its behaviour may not be consistent within an instance if it calls a method that is changed within the prototype