常量 vs 内联字符串字面值,编译优化。

15

这个问题并不是一个实际问题,而是基于代码结构对V8优化探究的一次探索。


我和另一个开发者正在讨论在V8编译优化中使用const字符串字面量 vs 内联字符串字面量的价值。当然,假设我们始终处于严格模式

下面是一些代码示例,以便更好地理解:

const

const NAME = "something";
function doSomething(s) {
  return NAME + s;
}

对比

内联字符串

function doSomething(s) {
  return "something" + s;
}

我们所达成的共识:
- 使用const能够更清晰地表述“魔法值”,从而方便代码维护。

我们存在分歧:

  • 我认为使用const可以让V8在编译时对您的代码进行优化,因为它保证了常量值不会改变。

  • 他认为使用inline-string字面值与const相同(如果不是更好,因为减少了间接性),对于V8编译器来说同样可以应用相同的优化,因为引用此类内联字符串时也可以这样做。

经过一番思考,我倾向于同意他的观点...字符串字面值将在每次调用函数时重新实例化,但这可以轻松地被V8进行优化并避免对后续调用产生影响。然而,我对编译器和编译器优化方面并不是很了解。

有人能够解释一下吗?

1个回答

22

我是一名V8开发人员。

即使在未经优化的代码中,内联字符串在所有函数调用之间共享,因此无需担心重新实例化的问题。 通常,JS引擎很容易发现字面量永远不会改变(因为它们是字面量!)。 const并不能提供你认为的那么多保证,因为JavaScript很复杂(例如:

function makeFunction(val) { 
  const NAME = val; 
  function doSomething(s) { 
    return NAME + s;
  }
  return doSomething;
}
var doSomething = makeFunction("something");
var doAnything = makeFunction("anything, really");

这里,const NAME不完全是你在直觉上所谓的常量;-)。

话虽如此,两种方法之间的差异可能太小而无关紧要。在你的代码中使用更有意义的方法。


附注:对于比字符串或数字更复杂的对象,情况会有所不同。例如:

function getSomething() { return "something"; }
function doSomething(s) { return getSomething() + s; }

显然比这个更高效:

function doSomething(s) {
  function getSomething() { return "something"; }
  return getSomething() + s;
}

因为在这种情况下,JS引擎必须创建“getSomething”的新实例(或花费大量的实现+计算工作来确定可以避免这样做;我不会依靠那个)。

区别在于对象标识的可观察性:

"a" === "a"  // true
(function f() {}) === (function f() {})  // false

在现代 JavaScript 引擎中,当创建 getSomething 时,它是否仍然使用相同的代码实例但在不同的闭包中,还是只是不同的实例? - l4m2
1
通常情况下,从同一源创建的多个闭包可以共享它们的代码,但它们是独立的“函数”对象(每个对象都包含指向共享代码的指针),并且这些对象具有相当大的大小。避免不必要的闭包通常是一种值得优化的方法。 - jmrk
如果您有多个函数共享相同的内联字符串,V8会怎么处理呢?如果它们都具有相同的值,它是否将字符串文字解析为相同的“引用”?就像某种字符串对象池一样? - John Leidegren
1
@JohnLeidegren 是的,V8会对JS源代码中出现的所有字符串字面量进行去重(我们称之为“内部化”)。 (动态生成的字符串不会立即进行重复消除;某些操作会根据需要对它们进行重复消除。) - jmrk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接