【发布时间】:2016-06-03 08:52:47
【问题描述】:
我正在开发一个 API 客户端,它允许在提供 foo 的 ID 时调用特定的 API 方法,如下所示:
apiClient.myApiMethod('myFooId', 'firstApiArg', 'nthApiArg');
为了方便开发者,我正在尝试实现自定义代理对象:
var myFoo = apiClient.registerFoo('myFoo', 'myFooId');
myFoo.myApiMethod('firstApiArg', 'nthApiArg');
经过一段时间的搜索,我认为 ES6 代理可能最适合这种情况,因为需要将 fooId 作为方法调用的第一个参数插入以支持两种工作方式。
因此,我创建了以下代码。如果调用Foo.myFoos 的对象属性(例如Foo.myFoos.example),则在_myFooItems 中搜索它,如果存在,则返回另一个代理对象。
现在,如果在 that 对象上调用了一个方法,则会在 Foo 的属性中搜索它,如果找到,则以 myFooId 作为其第一个参数调用 Foo 方法。
也就是说,你应该可以Foo.myFoos.example.parentMethodX('bar', 'baz')。
var Foo = function() {
// parent instance
_self = this;
// custom elements dictionary
_myFooItems = {};
// to call parent methods directly on custom elements
this.myFoos = Object.create(new Proxy({}, {
// property getter function (proxy target and called property name as params)
get: function(target, myFooName) {
// whether called property is a registered foo
if (_myFooItems.hasOwnProperty(myFooName)) {
// create another proxy to intercept method calls on previous one
return Object.create(new Proxy({}, {
// property getter function (proxy target and called property name as params)
get: function(target, methodName) {
// whether parent method exists
if (_self.hasOwnProperty(methodName)) {
return function(/* arguments */) {
// insert custom element ID into args array
var args = Array.prototype.slice.call(arguments);
args.unshift(_myFooItems[ myFooName ]);
// apply parent method with modified args array
return _self[ methodName ].apply(_self, args);
};
} else {
// parent method does not exist
return function() {
throw new Error('The method ' + methodName + ' is not implemented.');
}
}
}
}
));
}
}
}
));
// register a custom foo and its ID
this.registerFoo = function(myFooName, id) {
// whether the foo has already been registered
if (_myFooItems.hasOwnProperty(myFooName)) {
throw new Error('The Foo ' + myFooName + ' is already registered in this instance.');
}
// register the foo
_myFooItems[ myFooName ] = id;
// return the created foo for further use
return this.myFoos[ myFooName ];
};
};
module.exports = Foo;
虽然如果您运行代码并尝试注册 foo(上面的代码在 Node>=6.2.0 中工作)会发生什么情况,但会引发以下错误:
> var exampleFoo = Foo.registerFoo('exampleFoo', 123456)
Error: The method inspect is not implemented.
at null.<anonymous> (/path/to/module/nestedProxyTest.js:40:31)
at formatValue (util.js:297:21)
at Object.inspect (util.js:147:10)
at REPLServer.self.writer (repl.js:366:19)
at finish (repl.js:487:38)
at REPLServer.defaultEval (repl.js:293:5)
at bound (domain.js:280:14)
at REPLServer.runBound [as eval] (domain.js:293:12)
at REPLServer.<anonymous> (repl.js:441:10)
at emitOne (events.js:101:20)
在花了很多时间思考为什么第二个代理甚至尝试调用一个方法(如果没有给它)之后,我最终放弃了。我希望 exampleFoo 是一个代理对象,如果被调用则接受 Foo 方法。
是什么导致了这里的实际行为?
【问题讨论】:
-
乍一看,这种架构很难理解。
bind方法不能满足您的需要吗? -
"我认为 ES6 代理可能最适合这种情况" - 不,绝对不是。使用简单的
class来关闭apiClient。请重新开始。 -
@Bergi JS 类没有添加任何功能。由于我不想为一些 200 多种 API 方法编写修改后的继承函数,并将 ID 作为它们的第一个参数,因此我需要一种动态调用它们的方法。据我所知,代理是拦截函数调用和修改参数列表同时保持动态属性名称的唯一方法。
-
@trincot 也试过了,但是我无法绑定到
Foo的当前实例(它包含 API 凭据和配置) -
@MoFriedrich:如果您有 200 多个 API 方法以 ID 作为其第一个参数,您可以轻松枚举原始类以动态创建新方法(API 也很臃肿)。您不需要也不应该在这里使用代理。
标签: javascript node.js ecmascript-6 es6-proxy