Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call an instance method from inside a closure?

Tags:

coffeescript

I'm trying to access an instance method inside of a map call, unfortunately my reference to the instance object is being redefined to Window. I'm not sure how to get a hold of my instance method:

class Test
  constructor: (@an_array) ->

  f: () ->
    @an_array.map (value) ->
      @a(value)

  a: (value) ->
    alert value

t = new Test [1, 2, 3]
t.f() // TypeError: Object [object Window] has no method 'a'

Here's a functional link to the above code

like image 308
Gavin Miller Avatar asked Feb 18 '23 18:02

Gavin Miller


2 Answers

There are various ways to deal with this.

The most common in CoffeeScript would be to use a fat arrow (=>) to produce a bound function:

@an_array.map (value) => @a(value)

Demo: http://jsfiddle.net/ambiguous/6BW8q/

The standard JavaScript approaches will also work (and sometimes would be necessary or more appropriate):

  1. Save a reference to @ so that you don't have to care what this is inside the callback function:

    _this = @
    @an_array.map (value) -> _this.a(value)
    

    Demo: http://jsfiddle.net/ambiguous/XhP4z/

    I tend to use _this instead of self as the name for this thing due to the existence of window.self and the interesting bugs that causes if you forget the var in JavaScript.

  2. Manually create a bound function using Function.bind, this isn't quite universally supported though:

    @an_array.map ((value) -> @a(value)).bind(@)
    

    Demo: http://jsfiddle.net/ambiguous/n2XnC/

  3. Use jQuery's $.proxy, Underscore's _.bind, or some other non-native bound function implementation:

    @an_array.map _((value) -> @a(value)).bind(@)
    

    Demo: http://jsfiddle.net/ambiguous/LAy9L/

Which one you choose depends on your environment and specific needs:

  1. If you're trying to bind a function that comes from elsewhere, then you can't use => so you'll need to use some variant of (2) or (3) above (or possibly Function.call or Function.apply).
  2. If you need both the inner and outer this at the same time then you'd go with (1).
  3. If you need to manually bind a function but you aren't sure that a native bind exists then you' probably end up with (3) and which branch of (3) would probably depend on which library you already have available.
  4. ...
  5. Profit.
like image 130
mu is too short Avatar answered Feb 20 '23 06:02

mu is too short


Figured out that I can do it by defining a variable to hold my reference to this:

class Test
  constructor: (@an_array) ->

  f: () ->
    self = @
    @an_array.map (value) ->
      self.a(value)

  a: (value) ->
    alert value

t = new Test [1, 2, 3]
t.f()  // raises 3 alerts: 1, 2, 3

Here's a working example.

This feels like a bit of a hack, so I'll leave the Q&A up for someone to school me in how this should be done. :D

like image 24
Gavin Miller Avatar answered Feb 20 '23 06:02

Gavin Miller