【发布时间】:2019-07-13 10:38:47
【问题描述】:
你好!如果类的实例是可调用的,有时 API 可以写得特别干净。当一个类具有比任何其他操作更常见的操作时,这似乎特别有用。
例如,考虑一个用于定义树的库,其中树中的每个 Node 都有一个值和一个子节点的索引列表:
let Node = function(value, children) { /* ... */ };
Node.prototype = { /* ... */ };
let root = new Node('root', [
new Node('child1', [
new Node('grandchild11', []),
new Node('grandchild12', [])
]),
new Node('child2', [
new Node('grandchild21', []),
new Node('grandchild22', [])
])
]);
我会说Node 有一个比任何其他操作都更常见的操作:在特定索引处获取一个孩子:
root.getChild(1); // Returns the "child2" node (0-based indexing)
我会说这个操作很常见,通过以下方式达到相同的结果会非常可读和干净:
root(1);
但是要启用这样的语法,root 必须是一个可调用对象(因此Node 构造函数需要返回一个可调用对象)。这样的功能在链接时会非常酷!:
root(0)(1); // Returns `grandchild12`
可以想象这样的语法可以传递其他类型,例如传递一个函数可以返回匹配搜索的节点:
root(node => node.value === 'child1')(node => node.value === 'grandchild11');
是否有一些聪明的(元编程?)技术可以让 javascript 的 new 关键字返回一个可调用对象,并简化这样的语法?
请注意,对于更复杂的 API,多态性成为一项重要功能!如果可能,我想保留对象的原型链。
注意:
Jsperf 比较可调用实例 (root(0)) 和实例方法 (root.getChild(0)) 似乎告诉我 (Chrome 72.0.3626) 可调用实例是 a tiny bit slower。
【问题讨论】:
-
可以在构造函数中显式返回函数...
-
您的构造函数可以返回一个带有节点属性的
function实例。如果构造函数被作为构造函数调用(使用new)并且它有一个显式的return返回any 类型的对象,那么该对象将用作new的结果自动构造的普通对象。 -
啊,这可能吗,不会丢失原型链?这可能正是我想要的!
-
Object.setPrototypeOf(function someCallableNodeObj(){}, nodeProto)(并确保nodeProto.__proto__是Function,或者在链中的某个位置。你可以使用nodeProto = Object.create(Function)或类似的方法) -
@apsillers
Function.prototype不是Function确切地说
标签: javascript metaprogramming