javascript 的 `new` 运算符可以 return 一个可调用的 object 吗?
Can javascript's `new` operator ever return a callable object?
你好!如果 classes 的实例是可调用的,有时 API 可以写得特别干净。当 class 有一个比任何其他操作都更常见的操作时,这似乎特别有用。
例如,考虑一个用于定义树的库,其中树中的每个 Node
都有一个值和一个包含 child 个节点的索引列表:
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
有一个比任何其他操作都更常见的操作:在特定索引处获得 child:
root.getChild(1); // Returns the "child2" node (0-based indexing)
我会说这个操作很常见,通过以下方式获得相同的结果将非常可读和干净:
root(1);
然而,要启用这样的语法,root
必须是可调用的 object(因此 Node
构造函数需要 return 可调用的 object).这样的功能在链接时会非常酷!:
root(0)(1); // Returns `grandchild12`
可以想象,使用这种语法可以传递其他类型,例如传递函数可以 return 匹配搜索的节点:
root(node => node.value === 'child1')(node => node.value === 'grandchild11');
是否有一些聪明的(元编程?)技术可以允许 javascript 的 new
关键字成为 return 一个可调用的 object,并促进像这样的语法?
注意,对于更复杂的API,多态性成为一个重要的特征!如果可能,我想保留 object 的原型链。
注意:
Jsperf 比较可调用实例 (root(0)
) 和实例方法 (root.getChild(0)
) 似乎向我 (Chrome 72.0.3626) 表明可调用实例是 a tiny bit slower。
当然,通过 new
的调用可以 return 任何类型的对象,包括函数。的确,使用 new
调用函数会自动用新创建的对象填充 this
,但您不需要将 this
作为构造函数的 return 值:只需 return
任何其他对象。
您真正想要的是 Node
是一种功能。只需让您的 Node
构造函数 return 具有适当属性和原型链的函数对象。您需要确保
- 构造函数的return值是一个实际的函数
- 该函数值的原型已手动更改为您的
Node
原型对象
- 您的
Node
原型对象继承自 Function.prototype
,以便您的 Node
实例获得像 call
和 bind
这样的函数方法
例如:
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; }
你好!如果 classes 的实例是可调用的,有时 API 可以写得特别干净。当 class 有一个比任何其他操作都更常见的操作时,这似乎特别有用。
例如,考虑一个用于定义树的库,其中树中的每个 Node
都有一个值和一个包含 child 个节点的索引列表:
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
有一个比任何其他操作都更常见的操作:在特定索引处获得 child:
root.getChild(1); // Returns the "child2" node (0-based indexing)
我会说这个操作很常见,通过以下方式获得相同的结果将非常可读和干净:
root(1);
然而,要启用这样的语法,root
必须是可调用的 object(因此 Node
构造函数需要 return 可调用的 object).这样的功能在链接时会非常酷!:
root(0)(1); // Returns `grandchild12`
可以想象,使用这种语法可以传递其他类型,例如传递函数可以 return 匹配搜索的节点:
root(node => node.value === 'child1')(node => node.value === 'grandchild11');
是否有一些聪明的(元编程?)技术可以允许 javascript 的 new
关键字成为 return 一个可调用的 object,并促进像这样的语法?
注意,对于更复杂的API,多态性成为一个重要的特征!如果可能,我想保留 object 的原型链。
注意:
Jsperf 比较可调用实例 (root(0)
) 和实例方法 (root.getChild(0)
) 似乎向我 (Chrome 72.0.3626) 表明可调用实例是 a tiny bit slower。
当然,通过 new
的调用可以 return 任何类型的对象,包括函数。的确,使用 new
调用函数会自动用新创建的对象填充 this
,但您不需要将 this
作为构造函数的 return 值:只需 return
任何其他对象。
您真正想要的是 Node
是一种功能。只需让您的 Node
构造函数 return 具有适当属性和原型链的函数对象。您需要确保
- 构造函数的return值是一个实际的函数
- 该函数值的原型已手动更改为您的
Node
原型对象 - 您的
Node
原型对象继承自Function.prototype
,以便您的Node
实例获得像call
和bind
这样的函数方法
例如:
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; }