javascript的`new`运算符可以返回可调用对象吗?

时间:2019-02-19 20:33:46

标签: javascript metaprogramming

Нello!如果可以调用类的实例,则有时可以特别干净地编写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

1 个答案:

答案 0 :(得分:2)

当然,通过new进行的调用可以返回任何类型的对象,包括函数。的确,使用new调用函数会自动用新创建的对象填充this,但您不必让this作为构造函数的返回值:只需{{1 }}任何其他对象。

您真正想要的是拥有return是一种功能。只需让您的Node构造函数返回具有适当属性和原型链的函数对象即可。您需要确保

  1. 构造函数的返回值是一个实际函数
  2. 该函数值的原型已手动更改为您的Node原型对象
  3. 您的Node原型对象继承自Node,以便您的Function.prototype实例获得Nodecall之类的Function方法

例如:

bind