How to call an instance method from inside a closu

2020-04-08 14:38发布

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

2条回答
做自己的国王
2楼-- · 2020-04-08 15:05

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.
查看更多
▲ chillily
3楼-- · 2020-04-08 15:07

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

查看更多
登录 后发表回答