【问题标题】:Dynamic class generation in CoffeeScriptCoffeeScript 中的动态类生成
【发布时间】:2011-11-23 08:02:55
【问题描述】:

在 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()

现在它可以正常工作了,米老鼠说 eek!狮子辛巴吼叫着!但它看起来已经有些复杂了。我想知道是否有更简单的方法来实现这个结果,也许是直接访问原型。还是我完全走错了路?

【问题讨论】:

  • 你真的需要不同的课程吗?似乎它们只是在某些属性的值上有所不同,那么只有一个类Animal 和一些实例呢?
  • 我也问过自己是否需要不同的课程。看来我有。我需要的不仅仅是 Animal 的实例。我需要大象的实例,斑马的实例等。所有斑马共享某些属性,所有大象共享其他属性。它们都具有一些基本的动物特性。而且我在设计时不知道这些事情,因为上帝的创造是在一个配置文件中(只能在运行时读取)。我发现只有一个类没有令人满意的方法来模拟这个。 (当然,真正的应用是不同的):)

标签: javascript oop closures coffeescript


【解决方案1】:

对于您的直接问题(即循环中的闭包不会捕获当前值,而是最新的值),有do 构造:

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

不知何故,我希望 CoffeeScript 能够自动解决这个问题,因为这是 JavaScript 中的常见错误,但至少对于 do,有一种简洁的编写方式。

【讨论】:

    【解决方案2】:

    由于 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}"
    

    【讨论】:

    • +1。在类定义(而不是在构造函数中)设置属性不需要循环中的额外闭包。看看生成的 JS 看看有什么区别。
    • 你知道以后有没有办法动态命名类或更改类名? class animal.name extends Animal 将类命名为 animal.name 而不是变量的值...
    • @thomasf1 要做到这一点,您需要使类成为某个对象的属性,例如animals[name] = class extends Animal
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多