mikemaccana
mikemaccana

Reputation: 123118

Is there a way to avoid using 'call' in Coffeescript?

I'm writing a simple Twitter client to play with coffeescript. I have an object literal with some functions that call each other via callbacks.

somebject =
  foo: 'bar'
  authenticateAndGetTweets: ->
    console.log "Authorizing using oauth"
    oauth = ChromeExOAuth.initBackgroundPage(this.oauthdetails)
    oauth.authorize( this.afterLogin.call this )
  afterLogin: ->
    this.getTweets(this.pollinterval)

This code works perfectly. Edit: actually this.afterlogin should be sent as a callback above, not ran immediately, as Trevor noted below.

If, within authenticateAndGetTweets, I removed the 'call' and just ran:

oauth.authorize( this.afterLogin )

and don't use 'call', I get the following error:

Uncaught TypeError: Object [object DOMWindow] has no method 'getTweets

Which makes sense, since 'this' in afterLogin is bound to the thing that initiated the callback rather than 'someobject' my object literal.

I was wondering if there's some magic in Coffeescript I could be doing instead of 'call'. Initially I thought using the '=>' but the code will give the same error as above if '=>' is used.

So is there a way I can avoid using call? Or does coffeescript not obviate the need for it? What made '=>' not work how I expected it to?

Thanks. I'm really enjoying coffeescript so far and want to make sure I'm doing things 'the right way'.

Upvotes: 4

Views: 1993

Answers (3)

Trevor Burnham
Trevor Burnham

Reputation: 77416

As matyr points out in his comments, the line

oauth.authorize( this.afterLogin.call this )

doesn't cause this.afterLogin to be called as a callback by oauth.authorize; instead, it's equivalent to

oauth.authorize this.afterLogin()

Assuming that you want this.afterLogin to used as a callback by oauth.authorize, megakorre's answer gives a correct CoffeeScript idiom. An alternative approach supported by many modern JS environments, as matyr points out, would be to write

oauth.authorize( this.afterLogin.bind this )

There's no CoffeeScript shorthand for this, partly because Function::bind isn't supported by all major browsers. You could also use the bind function from a library like Underscore.js:

oauth.authorize( _.bind this.afterLogin, this )

Finally, if you were to define someobject as a class instead, you could use => to define afterLogin such that it's always bound to the instance, e.g.

class SomeClass
  foo: 'bar'
  authenticateAndGetTweets: ->
    console.log "Authorizing using oauth"
    oauth = ChromeExOAuth.initBackgroundPage(this.oauthdetails)
    oauth.authorize(this.afterLogin)
  afterLogin: =>
    this.getTweets(this.pollinterval)

someobject = new SomeClass

Upvotes: 3

megakorre
megakorre

Reputation: 2223

you can put a lambda in the function call like so

auth.authorize(=> @afterLogin())

Upvotes: 3

Maz
Maz

Reputation: 3375

You have to use either the call or apply methods because they set the scope of the function (the value of this). The error results because the default scope is the window object.

Upvotes: 0

Related Questions