For example:
function makeFunc(a,b,c,d,e) {
return () => {
if (a) { /* do something expensive not referencing b,c,d,e */ }
if (b) { /* do something expensive not referencing a,c,d,e */ }
if (c) { /* do something expensive not referencing a,b,d,e */ }
if (d) { /* do something expensive not referencing a,b,c,e */ }
if (e) { /* do something expensive not referencing a,b,c,d */ }
}
}
const func = makeFunc(true, false, false, false, false)
for ( let i=0; i < 100_000; i++) func()
I'm hoping that, in the example, V8 would optimize away the if (x)
tests on the closure, emitting just the
/* do something expensive not referencing b,c,d,e */
after if (a)
to be performed 100,000 times.
Yes, V8 optimizes this, as long as the variables from the outer context are never written to (meaning: as long as no code exists that could ever write to them).
Here is a counter-example where the optimization can't happen because the context variables aren't constants:
function makeFuncs(a, b, c, d e) {
return {
f1: () => { if (a) { console.log("a was true"); }},
f2: () => { a = !a; }
}
}
let funcs = makeFuncs(true);
funcs.f1(); // prints
funcs.f2();
funcs.f1(); // doesn't print
Clearly, when optimizing f1
on its own, the compiler can't just take the current value of a
and assume that it'll never change.
But as long as f2
with its a = ...
assignment doesn't exist, V8 realizes that a
is never written to, so the compiler can safely consider it to be constant. So in the OP's example, the if
checks do get optimized out.
(Thanks to @Dada for doing most of the investigation here!)