9

I would like to nest a number of functions inside a class property as shown below.
Unfortunately, they won't get access to the main scope of the class.

Can I solve this without passing each nested function a reference to this?

class myClass

  constructor: -> @errors = []

  doSomething: -> @errors.push "I work as expected"

  functions:
    doStuff: ->
      @errors.push "I cant access @errors" # => TypeError: Cannot call method 'push' of undefined

    ugly: (context) ->
      context.errors.push "It works, but I am ugly" # Works fine but requires scope injection

Non-working alternative using suggested fat arrow:

class myClass
  constructor: ->
    @errors = []

    @functions:
      doStuff: =>
        @errors.push "I wont work either" # TypeError: Cannot call method 'toString' of undefined

Optional alternative, which doesn't write to the global this.errors property:

class myClass

  constructor: ->

    @functions =
      errors: []
      doStuff: ->
        @errors.push "I will write to functions.errors only"
6
  • Binding them to this/@ in the constructor? Commented Oct 20, 2012 at 10:20
  • You mean constructor: -> @errors = [] @functions: doStuff -> ... ? Commented Oct 20, 2012 at 10:22
  • Maybe using the fat arrow => coffeescript.org/#fat_arrow I'm not a coffeescript master, sorry :) Commented Oct 20, 2012 at 10:24
  • Me neither. Tried fiddling around with it but couldn't get it working though. Commented Oct 20, 2012 at 10:25
  • Updated my post with how I tried using it. Commented Oct 20, 2012 at 10:33

2 Answers 2

3

In JavaScript (as the result, CoffeeScript too), methods use this of the object that contains method.

method()                  // this == globalObject
object.method()           // this == object
Math.random()             // this == Math

This usually works well, unless you deal with example like yours:

object.functions.method() // this == object.functions

When dealing with JavaScript, I would avoid having namespace for functions - it doesn't play well, even with workarounds. For example, you could try putting reference to this object in object.functions, so any function in object.functions would have access to it.

class MyClass
  constructor: ->
    @errors = []
    @functions.self = this

  doSomething: ->
    @errors.push "I work as expected"

  functions:
    alsoDoSomething: ->
      @self.errors.push "Also works!"

This appears to work at first, but could be confusing when you are using properties like apply or call on it, obj1.functions.alsoDoSomething.call(obj2) won't work as the obj2 is not correct object (user should do obj2.functions instead which can be confusing).

The real solution is: don't. JavaScript isn't intended for abuse like this. All object methods should be directly in object prototype. If you have object in it, all methods of it aren't methods of your object.

Sign up to request clarification or add additional context in comments.

Comments

2

As an addendum to GlitchMr's answer, I'll explain why each of your attempts failed.

  1. The functions object is declared on the prototype, so @errors is compiled to myClass.errors. However, the errors object is declared as an instance member, not a prototype member.
  2. You define functions using CoffeeScript's function notation, when it should be an object. The error message is a CoffeeScript compiler error; after fixing this syntax error, it works like it should!
  3. You preface this example with why it doesn't work, so I won't tell you twice!

Here is an example of the correct use of the fat arrow for this circumstance.

class MyClass

  constructor: ->
    @errors = []
    @functions = 
      doStuff: =>
        @errors.push "I can and do access @errors"

c = new MyClass
c.functions.doStuff()
console.log c.errors # ["I can and do access @errors"]

Hopefully this helped demystify the errors and show the power of CoffeeScript's fat arrow!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.