Javascript CoffeeScript类和nosuchmethod

Javascript CoffeeScript类和nosuchmethod,javascript,coffeescript,metaprogramming,ecmascript-harmony,Javascript,Coffeescript,Metaprogramming,Ecmascript Harmony,假设在底层Javascript引擎中存在,如何构造CoffeeScript超类,从而扩展它以允许类定义noSuchMethod方法(或methodMessing) 如果类(或其超类)没有请求的方法,则将使用名称和参数列表调用该方法。问得好=D (注意:我只在Firefox中测试过,因为它似乎是唯一支持Harmony代理的浏览器。) 这似乎适用于缺少的属性: class DynamicObject propertyMissingHandler = get: (target, name

假设在底层Javascript引擎中存在,如何构造CoffeeScript超类,从而扩展它以允许类定义noSuchMethod方法(或methodMessing)

如果类(或其超类)没有请求的方法,则将使用名称和参数列表调用该方法。

问得好=D

(注意:我只在Firefox中测试过,因为它似乎是唯一支持Harmony代理的浏览器。)

这似乎适用于缺少的属性:

class DynamicObject

  propertyMissingHandler =
    get: (target, name) ->
      if name of target
        target[name] 
      else
        target.propertyMissing name

  constructor: ->
    return new Proxy @, propertyMissingHandler

  # By default return undefined like a normal JS object.
  propertyMissing: -> undefined

class Repeater extends DynamicObject
  exited: no
  propertyMissing: (name) ->
    if @exited then "#{name.toUpperCase()}!" else name

r = new Repeater
console.log r.hi     # -> hi
console.log r.exited # -> false. Doesn't print "exited" ;)
r.exited = yes
console.log r.omg    # -> OMG!
现在,它可以工作了,但是它有一个小的警告:它依赖于一个“其他类型”的构造函数。也就是说,DynamicObject的构造函数返回的不是DynamicObject实例(它返回包装实例的代理)。其他类型的构造函数有微妙的和不太微妙的问题

例如,上述方法有效(在CoffeeScript 1.4中),但这仅仅是因为为Repeater生成的构造函数返回调用超级构造函数的结果(因此返回代理对象)。如果中继器有不同的构造函数,它将无法工作:

class Repeater extends DynamicObject
  # Innocent looking constructor.
  constructor: (exited = no) ->
    @exited = exited
  propertyMissing: (name) ->
    if @exited then "#{name.toUpperCase()}!" else name

console.log (new Repeater yes).hello # -> undefined :(
必须显式返回调用超级构造函数的结果才能使其工作:

  constructor: (exited = no) ->
    @exited = exited
    return super
因此,由于其他类型化构造函数有点混乱/破碎,我建议避免使用它们,并使用类方法来实例化这些对象,而不是使用
new

class DynamicObject

  propertyMissingHandler =
    get: (target, name) ->
      if name of target
        target[name] 
      else
        target.propertyMissing name

  # Use create instead of 'new'.
  @create = (args...) ->
    instance = new @ args...
    new Proxy instance, propertyMissingHandler

  # By default return undefined like a normal JS object.
  propertyMissing: -> undefined

class Repeater extends DynamicObject
  constructor: (exited = no) ->
    @exited = exited
    # No need to worry about 'return'
  propertyMissing: (name) ->
    if @exited then "#{name.toUpperCase()}!" else name

console.log (Repeater.create yes).hello # -> HELLO!

现在,对于缺少的方法,为了拥有与问题中请求的相同接口,我们可以在代理处理程序中执行类似的操作,但是当目标没有具有该名称的属性时,它不会直接调用特殊方法(propertyMissing),而是返回一个函数,该函数反过来调用特殊方法(方法缺失):


不幸的是,我找不到一种方法来区分代理处理程序中的属性访问和方法调用,以便DynamicObject更智能,并相应地调用propertyMissing或methodMissing(尽管如此,这是有意义的,因为方法调用只是在函数调用之后的属性访问)

如果我必须选择并使DynamicObject尽可能灵活,我会选择propertyMissing实现,因为子类可以选择如何实现propertyMissing,并将缺少的属性视为一种方法或不作为一种方法。上面根据propertyMissing实现的命令行示例如下:

class CommandLine extends DynamicObject
  cd: (path) ->
    # Usually 'cd' is not a program on its own.
    console.log "Changing path to #{path}" # TODO implement me
  propertyMissing: (name) ->
    (args...) ->
      command = "#{name} #{args.join ' '}"
      console.log "Executing command '#{command}'" 
有了它,我们现在可以混合从同一基类继承的中继器和命令行(多么有用!=p):


我不知道1.4中的Ctor问题,谢谢你指出。谢谢你探索这一点。这可能会让人不高兴,但从构造函数返回代理是一个很好的技巧。@koops是的,这很好,但我更希望看到一个正确扩展/继承代理的解决方案。扩展代理的问题,至少是irefox提供的是,代理的行为似乎不像普通的函数/构造函数;您可以执行
新建代理(目标,处理程序)
,但不能执行
代理。应用(此,目标,处理程序)
(在DynamicObject构造函数中执行此操作会更好)我还尝试将DynamicObject.prototype作为代理的一个实例(这听起来是一种合理的扩展方式),但这一点在各地爆发了。
class CommandLine extends DynamicObject
  cd: (path) ->
    # Usually 'cd' is not a program on its own.
    console.log "Changing path to #{path}" # TODO implement me
  propertyMissing: (name) ->
    (args...) ->
      command = "#{name} #{args.join ' '}"
      console.log "Executing command '#{command}'" 
cl = CommandLine.create()
r = Repeater.create yes
cl.echo r['hello proxies'] # -> Executing command 'echo HELLO PROXIES!'