javascript的"new"运算符可以返回可调用对象吗? [英] Can javascript's `new` operator ever return a callable object?
问题描述
Н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)可调用实例是解决方案
当然,通过new
进行的调用可以返回任何类型的对象,包括函数.的确,用new
调用函数会自动用新创建的对象填充this
,但是您不必让this
作为构造函数的返回值:只需return
任何其他对象.>
您真正想要的是具有Node
是一种功能.只需让您的Node
构造函数返回具有适当属性和原型链的函数对象.您需要确保
- 构造函数的返回值是一个实际函数
- 该函数值的原型已手动更改为您的
Node
原型对象 - 您的
Node
原型对象继承自Function.prototype
,因此您的Node
实例获得诸如call
和bind
之类的Function方法.
例如:
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; }
Нello! Sometimes an API could be written especially cleanly if instances of classes are callable. This seems to be especially useful when a class has an operation which is much more common than any other operation.
For example, consider a library for defining Trees, where each Node
in the tree has a value and an indexed list of child nodes:
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', [])
])
]);
I would say that a Node
has an operation which is much more common than any other operation: to get a child at a particular index:
root.getChild(1); // Returns the "child2" node (0-based indexing)
I would say that this operation is so common, it would be very readable and clean to achieve the same result via the following:
root(1);
However to enable syntax like this, root
would have to be a callable object (and therefore the Node
constructor would need to return a callable object). Such functionality would be really cool when chained!:
root(0)(1); // Returns `grandchild12`
It's imaginable that with such syntax other types could be passed, for example passing a function could return the node which matches the search:
root(node => node.value === 'child1')(node => node.value === 'grandchild11');
Is there some clever (metaprogramming?) technique which could allow javascript's new
keyword to return a callable object, and facilitate syntax such as this?
Note that for more complex APIs, polymorphism becomes an important feature! I'd like to preserve the object's prototype chain, if possible.
NOTE:
Jsperf comparing callable instances (root(0)
) and instance methods (root.getChild(0)
) seems to shows me (Chrome 72.0.3626) that callable instances are a tiny bit slower.
Sure, a call via new
can return any kind of object, including functions. It is true that calling a function with new
will automatically fill this
with a newly-created object, but you need not have this
be the return value of your constructor: simply return
any other object.
Really what you want here is have a Node
be a kind of function. Simply have your Node
constructor return a function object with the appropriate properties and prototype chain. You'll need to ensure
- The return value of the constructor is an actual function
- The prototype of that function value has been manually changed to your
Node
prototype object - Your
Node
prototype object inherits fromFunction.prototype
so that yourNode
instances get Function methods likecall
andbind
For example:
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; }
这篇关于javascript的"new"运算符可以返回可调用对象吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!