我最近需要实现这样的东西来跟踪一些复杂的面向对象递归的东西。基本上,我想让一种方法“可追溯”而不会造成太多污染;所以也许解决方案也可以在这里应用。
首先,添加一个使其他函数可追溯的函数:
Function::trace = do ->
makeTracing = (ctorName, fnName, fn) ->
(args...) ->
console.log "#{ctorName}:#{fnName}"
fn.apply @, args
(arg) ->
for own name, fn of arg
@prototype[name] = makeTracing @name, name, fn
然后,要使用它,只需@trace
在要跟踪的每个方法之前添加一个:
class MyClass
@trace methodA: ->
@methodB 42
@trace methodB: ->
console.log "method b called with #{n}"
或者只添加一次@trace,然后将所有可跟踪的方法再缩进一层:
class MyClass
@trace
methodA: ->
@methodB 42
methodB: (n) ->
console.log "method b called with #{n}"
如您所见trace
,这有点滥用 CoffeeScript 的语法。method: -> 'foo'
a 内部class MyClass
被解释为方法定义。但是@trace method: -> 'foo'
被解释为调用trace
函数MyClass
(它是一个Function
实例,我们已经向其中添加了trace
函数)传递一个带有一个method
键的文字对象。在 JavaScript 中,它类似于this.trace({method: function() {return 'foo';}})
.
该trace
函数将只获取该对象并迭代它的键(方法名称)和值(方法),并将函数添加到MyClass
记录其调用的原型中,然后调用原始方法。
无论如何,输出(new MyClass).methodA()
将是:
MyClass:methodA
MyClass:methodB
method b called with 42
但是,此解决方案不适用于构造函数,因为它们不仅仅是普通方法。
你可以很喜欢这个。您还可以记录传递给每个方法的参数、返回值,如果您愿意,甚至可以为嵌套调用添加缩进(如果您需要调试复杂的问题 =D,生成的跟踪会非常有用)。
更新:作为一个更有趣的例子,这里有一个典型的复合图案例子的迷你版,几何图形和图形组:http: //jsfiddle.net/2YuE7/具有更有趣的跟踪功能。所有的数字都理解这个move
方法。如果我们有这个复合图形并调用move
它:
f = new Composite [
new Rectangle 5, 10, 3, 4
new Composite [
new Circle 0, 0, 2
new Circle 0, 0, 4
new Circle 0, 0, 6
]
]
f.move 2, 3
跟踪输出为:
(Composite[Rectangle[5,10,3,4],Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]]).move(2, 3)
| (Rectangle[5,10,3,4]).move(2, 3)
| -> Rectangle[7,13,3,4]
| (Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]).move(2, 3)
| | (Circle[0,0,2]).move(2, 3)
| | -> Circle[2,3,2]
| | (Circle[0,0,4]).move(2, 3)
| | -> Circle[2,3,4]
| | (Circle[0,0,6]).move(2, 3)
| | -> Circle[2,3,6]
| -> Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]
-> Composite[Rectangle[7,13,3,4],Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]]