可以使用javascript';s`new`运算符是否返回过可调用对象?

可以使用javascript';s`new`运算符是否返回过可调用对象?,javascript,metaprogramming,Javascript,Metaprogramming,你好!有时,如果类的实例是可调用的,则可以特别干净地编写API。当一个类有一个比任何其他操作更常见的操作时,这似乎特别有用 例如,考虑一个定义树的库,其中树中的每个节点< /代码>都有一个值和索引的子节点列表: let Node = function(value, children) { /* ... */ }; Node.prototype = { /* ... */ }; let root = new Node('root', [ new Node('child1', [ ne

你好!有时,如果类的实例是可调用的,则可以特别干净地编写API。当一个类有一个比任何其他操作更常见的操作时,这似乎特别有用

例如,考虑一个定义树的库,其中树中的每个<代码>节点< /代码>都有一个值和索引的子节点列表:

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', [])
  ])
]);
我想说的是,
节点
有一个比任何其他操作都更常见的操作:在特定索引处获取子节点:

root.getChild(1); // Returns the "child2" node (0-based indexing)
我想说的是,这种操作非常常见,通过以下方法可以获得相同的结果:

root(1);
但是,要启用这样的语法,
root
必须是可调用对象(因此
节点
构造函数需要返回可调用对象)。这样的功能在链接时会非常酷!:

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)表明可调用实例是可调用的。

当然,通过
new
调用可以返回任何类型的对象,包括函数。确实,使用
new
调用函数将自动使用新创建的对象填充
this
,但您不需要将
this
作为构造函数的返回值:只需
return
任何其他对象即可

这里真正需要的是有一个
节点
作为一种函数。只需让
节点
构造函数返回具有适当属性和原型链的函数对象。你需要确保

  • 构造函数的返回值是一个实际函数
  • 该函数值的原型已手动更改为
    节点
    原型对象
  • 您的
    节点
    原型对象继承自
    函数。原型
    ,这样您的
    节点
    实例就可以获得函数方法,如
    调用
    绑定
  • 例如:

    function Node(value) {
        // build the function to return
        var returnFunc = function getChild() { /*...*/ };
    
        // set instance properties like `value`, `children`, whatever
        returnFunc.value = value;
    
        // inherit Node methods and make `this instanceof Node` be true
        Object.setPrototypeOf(returnFunc, Node.prototype);
    
        // or non/barely-standard, but with older support:
        //    returnFunc.__proto__ = Node.prototype
    
        // you must explicitly `return` a value, or else `this` is used
        return returnFunc;
    }
    
    // Node instances should inherit Function methods like `call` and `bind`
    // so Node must be a prototypal child of Function
    Node.prototype = Object.create(Function.prototype);
    
    // set Node inherited prototype methods
    Node.prototype.getChild = function() { /*...*/ }
    
    // optional: change the `toString` representation to reflect Node `value` instead of function code
    Node.prototype.toString = function() { return "I'm a Node: " + this.value; }
    

    您可以在构造函数中显式返回一个函数……构造函数可以返回一个
    函数
    实例,该实例附带有节点属性。如果构造函数作为构造函数调用(使用
    new
    ),并且它有一个显式的
    返回
    ,返回任何类型的对象,那么该对象将被用作
    new
    的结果,而不是自动构造的普通对象。啊,这是可能的,而不会丢失原型链吗?那可能正是我要找的
    Object.setPrototypeOf(function someCallableNodeObj(){},nodeProto)
    (并确保
    nodeProto.\uuuu proto\uuuu
    function
    ,或在链中的某个位置。您可以执行
    nodeProto=Object.create(function)
    或类似操作)@apsillers
    function.prototype
    确切地说,不是
    function