最近我进入了javascript生态系统。在使用javascript的回调一段时间之后,我开始问自己javascript解释器是否能够对回调参数进行条件评估。我们来看以下两个例子:
var a = 1;
var b = 2;
// example 1
abc.func(a, b, function (res) {
// do something with res
});
// example 2
abc.func(a, b, function () {
// do something
});
据我所知,Javascript使用arguments
对象来跟踪传递给函数的内容。这与函数定义无关。所以假设:
abc.func = function (a, b, cb) {
// do stuff
var res = {};
// Expensive computation to populate res
cb(res);
}
在两个示例(1,2)中,res
对象将传递给arguments[0]
。在示例1 res === arguments[0]
中,因为定义了res
参数。
我们假设计算res
很昂贵。在示例1中,可以通过此计算,因为使用了res
对象。在示例2中,由于未使用res
对象,因此进行该计算确实没有意义。虽然,由于需要填充arguments
对象,因此在两种情况下都会完成填充res
的计算。这是对的吗?
假设这是真的,这似乎是(可能)巨大的浪费。为什么要计算一些超出范围并被垃圾收集的东西?想想那里使用回调的所有库。他们中的很多人都将多个参数发送回回调函数,但有时候都没有使用它们。
有没有办法防止这种行为。基本上使Javascript解释器足够智能,不会计算那些将变成未使用的参数的特定变量?因此,在示例2中,res
对象实际上不会被计算,因为它永远不会被实际使用。
据我所知,在此之前使用了以下内容:
function xyz(a, b /*, rest */)
// use arguments to iterate over rest
}
因此,默认情况下仍然可以计算这些参数。现在让我们期待ECMAScript 2015.这将包括要定义的...rest
参数。那么对于支持新版本的引擎,有没有办法启用条件评估?这会更有意义,因为现在有一种方法可以明确地要求评估并将所有额外的参数传递给函数。
答案 0 :(得分:2)
不,JavaScript不是一种懒惰的逐个调用语言。这主要是因为表达式可能有副作用,ES标准要求它们按程序员期望的顺序执行。
是的,JS引擎很聪明。如果他们确实检测到代码没有执行副作用,并且其结果未在任何地方使用,则只转储它们(dead code elimination)。我不确定这是否跨越函数边界,我想它不会,但如果你在热代码路径中并且调用内联,它可能会被优化。
因此,如果您知道自己正在进行大量计算,则可能需要通过传递thunk来使其显式延迟。在急切评估的语言中,这通常仅由不带参数的函数表示。在你的情况下:
abc.func = function (a, b, cb) {
// do stuff
var res = {};
cb(() => {
// Expensive computation to populate res
return res;
});
}
// example 1
abc.func(a, b, function(getRes) {
var res = getRes();
// do something with res
});
// example 2
abc.func(a, b, function() {
// no heavy computation
// do something
});
答案 1 :(得分:0)
你无法在解释器级别上这样做,确定计算参数是否依赖于计算另一个参数是不可行的,即使你可能会为用户创建不一致的行为。因为将变量传递给函数非常便宜,所以这将成为一种毫无意义的练习。
它可以在功能级别上完成 - 如果你想要,你可以将回调的预期参数作为参数传递给函数,从而根据参数增加函数的行为,这是常见的。