我试图从 https://javascript.info/call-apply-decorators
解决任务#1事情是这样的:
创建一个装饰器间谍(func),它应该返回一个包装器,该包装器将所有对函数的调用保存在其调用属性中。
每个调用都保存为参数数组。
例如:
function work(a, b) { alert( a + b ); // 'work' is an arbitrary function or method } work = spy(work); work(1, 2); // 3 work(4, 5); // 9 for (let args of work.calls) { alert( 'call:' + args.join() ); // "call:1,2", "call:4,5" }
解决办法是:
function spy(func) { function wrapper(...args) { wrapper.calls.push(args); return func.apply(this, args); } wrapper.calls = []; return wrapper; }
在给定的解决方案(以及一般的闭包)中,我不明白的是分配给包装器的属性(wrapper.calls)现在也可用于“工作”功能。
使用
work = spy(work)
行,我们为“work”函数创建了包装器。现在后者的价值是
ƒ wrapper(...args) {
wrapper.calls.push(args);
return func.apply(this, args);
}
好的,“work”的值就是“wrapper”函数。并且“wrapper”具有 .calls 属性。但这个属性怎么也存在“工作”呢?
弄清楚函数分配和闭包如何相互作用需要时间。尽我所能解释一下我的理解:
您将spy(work)的结果分配给work,因此work现在被spy返回的包装函数替换。在 JavaScript 中,函数是对象,因此这种替换本质上改变了 work 引用的内容。当您从间谍(工作)调用返回它时,它是包装函数。
在spy内部,创建了一个闭包,其中保存对原始工作函数func的引用。这允许包装函数在其本地范围内访问和调用原始函数。所以,基本上你最初创建的工作函数并没有被删除,你可以用不同的对象引用来指向它。
原来的work函数在闭包中保存为func,而work本身现在指向包装器。
通过将间谍的结果分配给工作,您可以有效地用从间谍返回的新对象(包装函数)替换工作。
新工作(现在是包装函数)保留对原始工作函数(在闭包中存储为 func )的访问。这允许包装器调用原始函数,同时跟踪其自己的属性(调用)。
本质上,将spy(work)分配给work意味着work现在指向包装函数,它具有原始函数之外的附加上下文和属性。