Scope in coffeescript classes

岁酱吖の 提交于 2019-12-08 15:59:14

问题


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"

回答1:


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.




回答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!



来源:https://stackoverflow.com/questions/12987400/scope-in-coffeescript-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!