Escopo em classes de coffeescript

9

Gostaria de aninhar várias funções dentro de uma propriedade de classe, conforme mostrado abaixo.
Infelizmente, eles não terão acesso ao escopo principal da aula.

Posso resolver isso sem passar a cada função aninhada uma referência a 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

Alternativa não funcional usando a seta de gordura sugerida:

class myClass
  constructor: ->
    @errors = []

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

Alternativa opcional, que não grava na propriedade global this.errors :

class myClass

  constructor: ->

    @functions =
      errors: []
      doStuff: ->
        @errors.push "I will write to functions.errors only"
    
por Industrial 20.10.2012 в 12:14
fonte

2 respostas

3

Em JavaScript (como resultado, CoffeeScript também), os métodos usam this do objeto que contém o método.

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

Isso geralmente funciona bem, a menos que você lide com um exemplo como o seu:

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

Ao lidar com JavaScript, evito ter namespace para funções - ele não funciona bem, mesmo com soluções alternativas. Por exemplo, você poderia tentar colocar referência a this object em object.functions , portanto, qualquer função em object.functions teria acesso a ela.

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

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

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

Parece funcionar primeiro, mas pode ser confuso quando você está usando propriedades como apply ou call , obj1.functions.alsoDoSomething.call(obj2) não funcionará como o objeto obj2 não está correto (o usuário deve fazer obj2.functions em vez disso, o que pode ser confuso).

A solução real é: não . JavaScript não é destinado a abuso como este. Todos os métodos de objeto devem estar diretamente no protótipo do objeto. Se você tiver algum objeto, todos os métodos não são métodos do seu objeto.

    
por Konrad Borowski 20.10.2012 / 13:42
fonte
2

Como um adendo à resposta da GlitchMr, explicarei por que cada uma das suas tentativas falhou.

  1. O objeto functions é declarado no protótipo, portanto @errors é compilado em myClass.errors . No entanto, o objeto errors é declarado como um membro da instância, não como um membro protótipo.
  2. Você define functions usando a notação de função do CoffeeScript, quando deveria ser um objeto. A mensagem de erro é um erro do compilador CoffeeScript; depois de corrigir este erro de sintaxe, ele funciona como deveria!
  3. Você começa o exemplo com o motivo de não funcionar, então não vou contar duas vezes!

Este é um exemplo do uso correto da seta de gordura para essa circunstância.

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"]

Espero que isso tenha ajudado a desmistificar os erros e mostrar o poder da flecha gorda do CoffeeScript!

    
por user1080806 22.10.2012 / 23:05
fonte