假设在底层的Javascript引擎中存在Harmony Proxies,那么如何构造CoffeeScript超类,以便扩展它将允许类定义noSuchMethod方法(或methodMessing)?
如果类(或其超类)没有请求的方法,将使用名称和参数列表调用该方法。
发布于 2013-01-25 12:04:03
问得好!=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实例(它返回包装该实例的代理)。其他类型的构造函数有微妙和不那么微妙的问题和they are not a very loved feature in the CoffeeScript community。
例如,上面的方法是有效的(在CoffeeScript 1.4中),但这只是因为为Repeater1.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),而是返回一个函数,该函数反过来调用特殊方法(methodMissing):
class DynamicObject2
methodMissingHandler =
get: (target, name) ->
return target[name] if name of target
(args...) ->
target.methodMissing name, args
# Use this instead of 'new'.
@create = (args...) ->
instance = new @ args...
new Proxy instance, methodMissingHandler
# By default behave somewhat similar to normal missing method calls.
methodMissing: (name) -> throw new TypeError "#{name} is not a function"
class CommandLine extends DynamicObject2
cd: (path) ->
# Usually 'cd' is not a program on its own.
console.log "Changing path to #{path}" # TODO implement me
methodMissing: (name, args) ->
command = "#{name} #{args.join ' '}"
console.log "Executing command '#{command}'"
cl = CommandLine.create()
cl.cd '/home/bob/coffee-example' # -> Changing path to /home/bob/coffee-example
cl.coffee '-wc', 'example.coffee' # -> Executing command 'coffee -wc example.coffee'
cl.rm '-rf', '*.js' # -> Executing command 'rm -rf *.js'不幸的是,我找不到一种方法来区分代理处理程序中的属性访问和方法调用,这样DynamicObject就可以更智能地相应地调用propertyMissing或methodMissing (尽管这是有意义的,因为方法调用只是简单的属性访问,然后是函数调用)。
如果我必须选择尽可能灵活的DynamicObject,我会选择propertyMissing实现,因为子类可以选择如何实现propertyMissing,并将缺少的属性作为方法来处理。上面用propertyMissing实现的CommandLine示例如下:
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}'" 有了这个,我们现在可以混合使用从同一个基类继承的中继器和CommandLines (多有用!=P):
cl = CommandLine.create()
r = Repeater.create yes
cl.echo r['hello proxies'] # -> Executing command 'echo HELLO PROXIES!'https://stackoverflow.com/questions/14513509
复制相似问题