很多JS的文章或者教程里都会提到内存分配。一种错误的说法是“基础类型存在栈里,Object存在堆里“。
首先,JavaScript这门语言本身不会规定到底怎么样的类型的值是在stack上,什么样的类型是在heap上 - 即使是类似于c/c++这种偏底层的语言,也不会在规范里定义stack或者heap。有的只是automatic storage duration 和 dynamic storage duration。问JavaScript的基础类型不是存在栈里还是堆里就像问JavaScript是compiled 还是 interpreted的。这个问题本身就是一个错误的问题。
语言的规范和实现是两个不同层面的抽象。在JavaScript里 {} !== {} 不是因为{} 和 {} 是在不同内存区块上。他们不一样是因为标准里规定了他们不一样。 你完全可以写一个JS的VM,里面所有的empty object都共享一个内存,然后用其他办法去区分他们的identify,比如一个存在其他地方的counter,只要你的实现是遵守了这个语言的static and runtime semantics就行 - the "as-if" rule
如果你真的好奇这些implementation details,就去看V8的官方blog或者看源码。
V8的官方Blog里面就有毫不含糊地正面回答过这个问题:
JavaScript values in V8 are represented as objects and allocated on the V8 heap, no matter if they are objects, arrays, numbers or strings. This allows us to represent any value as a pointer to an object.
所有东西都是在heap上 (除了小整数(smi)是直接encoded在pointer里的)
想自己去观察V8的memory layout的话,可以用Chrome DevTools的memory profile。但其实这样还是不够准确,devtools里展示的heap snapshot是经过一个raw_address :object ID map 处理的。所以想要知道最准确的答案还是得去看源码,或者用native debugger。
我在自己的博客里有粗略地去讨论不同情况下JS变量存的地方。有兴趣的可以去看。
这些篇文章不是为了强调技术本身。这里太多大佬比我强了。知道这些implementation details也多半没有什么实际的用处,除非你自己要去写一个JS的VM。
我真正想说的是,如果自己没有深入了解过JS的底层实现,你可以完全的呆在语言规范这层,从JavaScript“内部”去学习和理解这门语言,你也可以写地一手好JS。很多人写JS教程的时候,喜欢附带一些自己道听途说的语言之外的东西 - 比如object是在堆上,primitive在栈上 - 好像这样就能让新手更好的掌握这门语言。对于这些朋友,我推荐他们去看看前端娱乐圈著名的Dan Abramov出的JS 教程 Just JavaScript。 Dan没有去瞎扯什么栈还是堆,传引用还是传值,而是从这门语言的“内部”,建立起一个简单易懂的mental model,让不同背景和基础的人都能理解和掌握这门语言。不是他不懂这些东西,而是从掌握这门语言的角度上来说,这些问题大都没有意义。