Динамическая генерация классов в CoffeeScript - PullRequest
4 голосов
/ 23 сентября 2011

Каков наилучший способ динамически создавать классы в CoffeeScript, чтобы впоследствии создавать экземпляры их объектов?

Я нашел способы сделать это, но я не уверен, может быть, есть даже лучший (или более простой) способ добиться этого. Пожалуйста, дайте мне знать ваши мысли о моем коде.

Давайте начнем с простых нединамических классов:

class Animal
  constructor: (@name) ->

  speak: ->
    alert "#{@name} says #{@sound}"

class Cat extends Animal
  constructor: (@name) ->
    @sound = "meow!"

garfield = new Cat "garfield"
garfield.speak()

Как и ожидалось, Гарфилд говорит мяу!

Но теперь мы хотим динамически генерировать классы для большего количества животных, которые определены следующим образом:

animalDefinitions = [
    kind:  'Mouse'
    sound: 'eek!'
  ,
    kind:  'Lion'
    sound: 'roar!'
  ]

Первая наивная попытка не удалась:

for animal in animalDefinitions
  animal.class = class extends Animal
    constructor: (@name) ->
      @sound = animal.sound

mutant = new animalDefinitions[0].class "mutant"
mutant.speak()

Животное, которое мы только что создали, mutant, должно быть мышью. Тем не менее, это говорит рев! Это потому, что animal.sound оценивается только тогда, когда мы создаем экземпляр класса. К счастью, из JavaScript мы знаем проверенный способ решить эту проблему: закрытие:

for animal in animalDefinitions
  makeClass = (sound) ->
    class extends Animal
      constructor: (@name) ->
        @sound = sound
  animal.class = makeClass(animal.sound)

mickey = new animalDefinitions[0].class "mickey"
mickey.speak()

simba = new animalDefinitions[1].class "simba"
simba.speak()

Теперь все работает по желанию, говорит Микки Маус. и симба лев говорит рев! Но это уже выглядит несколько сложно. Мне интересно, есть ли более простой способ достичь этого результата, возможно, путем прямого доступа к прототипу. Или я совершенно не на том пути?

Ответы [ 2 ]

3 голосов
/ 23 сентября 2011

Поскольку sound является значением по умолчанию для экземпляра Animal, вы можете установить его в качестве свойства для определения класса:

class Cat extends Animal
    sound: 'meow!'

garfield = new Cat "garfield"
garfield.speak() # "garfield says meow!"

, затем

for animal in animalDefinitions
    animal.class = class extends Animal
        sound: animal.sound

mutant = new animalDefinitions[0].class "mutant"
mutant.speak() # "mutant says eek!"

Если вы хотите sound для переопределения, вы можете сделать

class Animal
    constructor: (@name, sound) ->
        @sound = sound if sound? 
    speak: ->
        console.log "#{@name} says #{@sound}"
2 голосов
/ 23 сентября 2011

Для вашей непосредственной проблемы (которая заключается в том, что замыкание в цикле захватывает не текущее значение, а последнее), существует конструкция do:

for animal in animalDefinitions
  do (animal) ->
    animal.class = class extends Animal
      constructor: (@name) ->
        @sound = animal.sound

Я как-то ожидал CoffeeScriptпозаботиться об этом автоматически, так как это распространенная ошибка в JavaScript, но по крайней мере с do есть краткий способ написать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...