JavaScript 中的代理模式






4.75/5 (4投票s)
只需一个函数调用,即可在 Javascript 中使用代理包装一个类。 这是在 node.js 中使用 mocha 轻松进行错误模拟的前身
引言
这是一个非常简单的脚本,允许您为 JavaScript 中的类创建一个代理。假设我们有一个类(将用作构造函数的函数)Math
,它定义了一些函数,例如 add
、subtract
、multiply
和 divide
。
function Math() {
this.IsMath = true;
}
Math.prototype = {
add: function(a, b) {
return a + b;
},
subtract: function(a, b) {
return this.add(a, -b);
},
multiply: function(a, b) {
return a * b;
},
divide: function(a, b) {
return a / b;
}
}
我们希望能够修改它的行为,以便我们可以使用 beforeFunction
事件和 afterFunction
事件来拦截此类中的每个函数调用。
我们可以决定对这两个函数做任何我们想做的事情。您可以访问此处的技巧,它利用此通用代理脚本允许您在 mocha (node.js) 中非常容易地模拟错误。 对于本文,我将定义一个简单的代理来记录函数调用(这是在 node.js 中使用 console.log 完成的,但可以很容易地针对浏览器代码进行修改)。
Using the Code
您只需要一个名为 createProxyDefinition
的函数。 此函数在 proxy.js 中定义,您可以从本文附加的文件中下载。 该函数采用四个参数,prefix
、beforeFunction
、afterFunction
和 more
。
beforeFunction
和 afterFunction
的含义非常明显。 每当调用目标类的函数时,这两个事件将相应地触发。 prefix
是您要创建的代理的唯一名称标识符,这将允许您将多个代理“添加”到同一个类。
beforeFunction
将被赋予三个参数,第一个是被调用的函数 name
,第二个是传递给该函数的 arguments
数组,第三个是参数名称的数组。 如果您从事件中返回值,则不会调用原始函数,而将使用您的返回值代替。
afterFunction
将被赋予四个参数,第一个是被调用的函数 name
,第二个是原始函数的返回值,第三个是传递给该函数的 arguments
数组,第四个是参数名称的数组。
more
是任何额外的对象,它将扩展传入的类的原型。
一个例子最能解释如何使用这个类。 我想创建一个简单的代理定义,它只打印出被调用的函数名称及其参数,这对于跟踪函数调用很有用。 另一个更有用的例子,也是我首先想写这篇文章的原因,是为了让我能够在编写 node.js 项目时进行完整的代码覆盖,请参考此处的技巧。
创建一个记录文件的代理定义 (node.js 模块)
var beforeFunction = function(name, args, paramsArray) {
if(!this.__levels) {
this.__levels = 0;
}
for(var i = 0; i < this.__levels; i++) {
process.stdout.write("\t");
}
console.log(" Entering: >> ", name, " : ", args);
this.__levels++;
};
var afterFunction = function(name, ret, args, paramsArray) {
this.__levels--;
for(var i = 0; i < this.__levels; i++) {
process.stdout.write("\t");
}
console.log(" Exiting: >> ", name)
}
var createCallsLogger = createProxyDefinition("callslogger", beforeFunction, afterFunction);
createCallsLogger
现在是一个接受任何构造函数并根据需要修改它的函数。
一个示例用法是
createCallsLogger(Math);
var math = new Math();
math.add(1, 2);
math.subtract(10, 3);
math.multiply(5, 5);
math.divide(16, 4);
现在对 math 进行的任何函数调用都将被记录到控制台,一个示例输出如下
usr/bin/node index.js
Entering: >> add : { '0': 1, '1': 2 }
Exiting: >> add
Entering: >> subtract : { '0': 10, '1': 3 }
Entering: >> add : { '0': 10, '1': -3 }
Exiting: >> add
Exiting: >> subtract
Entering: >> multiply : { '0': 5, '1': 5 }
Exiting: >> multiply
Entering: >> divide : { '0': 16, '1': 4 }
Exiting: >> divide
Process finished with exit code 0
幕后
createProxyDefinition
函数接受一个类定义(构造函数),并遍历其所有原型函数,用一个新函数替换每个函数,该函数执行所需的调用和事件的委托。 此函数在附加的文件 proxy.js 中。 粗略地说,这就是它在伪代码中所做的事情。
function __createProxy(constructor, proxyInstanceDefinition, defPrefix)
{
if (proxy already added)
return;
for each function in constructor.prototype
{
var newName = __ + functionname;
copy original function reference to a new function with name "NewName"
create a new function to replace the current.
The new function will call original function)
}
}
关注点
此代理创建方法并非旨在在生产代码中使用,理想情况下,它用于快速模拟或利用其他类,例如使用它来模拟 node.js 中的错误以实现完全覆盖的情况。 不建议在生产代码中使用它的原因是,它基本上为每个要覆盖的函数创建新的函数实例,因此理想情况下,在生产环境中,您应该有自定义编写的代码来做这样的事情,以实现您想要的确切目标,而无需额外的重定向和内存使用开销。
历史
- 2013 年 11 月 21 日:初始版本