JavaScript指针/引用的疯狂。 有人可以解释这个吗?

JavaScript通过引用传递对象。 这很有道理。 但是一旦你开始操作这些对象,所有事情都会以一种看起来不直观的方式进行。 让我举个例子:

var a, b; a = {} b = a; a['one'] = {}; console.log( JSON.stringify(a) ); // outputs: {"one":{}} console.log( JSON.stringify(b) ); // outputs: {"one":{}} 

这一切都很好,因为现在b有一个指向b的指针,所以期望给a赋值的东西也会影响b

但是,如果我这样做:

 a = a['one']; console.log( JSON.stringify(a) ); // outputs: {} console.log( JSON.stringify(b) ); // outputs: {"one":{}} 

这对我来说是令人惊讶的。 我希望ab仍然是相同的(因为a['one']先前被设置为{}a被设置为a['one'] )。

但事实并非如此。 当它被分配给新的东西的时候,它似乎失去了对b引用,但是b在失去对b引用之前保持了a被设置的值。

但是,如果我这样做:

 a['two'] = 2; console.log( JSON.stringify(a) ); // outputs: {"two":2} console.log( JSON.stringify(b) ); // outputs: {"one":{"two":2}} 

什么? a显然已经失去了对b的引用,但b似乎仍然有一些提及a

空对象是否指向内存中的某个位置,以便引用它的每个variables现在指向同一个地方?

有人能坚定地把握这个解释给我吗?

按照您的示例行:

 a = {} 

现在引用新的对象。

 b = a; 

b现在引用与引用相同的对象。 请注意,它没有引用a

 a['one'] = {}; 

新对象现在有一个引用另一个新对象的索引'one'

当你这样做

 a = a['one']; 

您正在设置a引用a['one'] ,这是您在创builda['one'] = {}时创build的新对象。 b仍然引用你用a = {}创build的对象。

当你说“ a失去了对b引用”时,你会混淆这个问题,因为a不是指b ,反之亦然。 ab是指对象 ,可以用来引用其他对象。 喜欢这个:

a = {}; b = a a = {}; b = a ,你明白了

 a \ \ { } / / b 

然后用a['one'] = {}得到

 a \ \ { one: { } } / / b 

然后用a = a['one']得到

 a - - - - \ { one: { } } / / b 

:P你正在下降到细节,我很高兴你问,因为你会明智的结束。

不要用指针来看待它,因为我认为这是你感到困惑的地方。 把它看作堆(或者只是“记忆”,如果你愿意的话)和符号表。

让我们先看看代码的前几行:

 var a, b; a = {} b = a; 

你在这里做的是在堆上创build一个对象,在符号表上创build两个符号。 它看起来像这样:


符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400000 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 | <object val 1> | +----------+-----------------+ 


这就是事情变得有趣的地方:对象有自己的“符号表”(通常这些只是哈希表,但把它称为符号表可以使它更清晰)。

现在,在你的下一个语句之后,你需要考虑三件事情:全局符号表, <object val 1>的符号表和堆。

运行以下行:

 a['one'] = {} 

现在看起来像这样:


全球符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400000 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

符号表<object val 1>的符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | one | 0x400004 | +--------+-----------------+ 

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 | <object val 1> | +----------+-----------------+ | 0x400004 | <object val 2> | <---we created a new object on the heap +----------+-----------------+ 


现在你运行下面的代码:

 a = a['one']; 

这似乎应该是一个微不足道的变化。 结果是:


全球符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400004 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

符号表<object val 1>的符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | one | 0x400004 | +--------+-----------------+ 

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 | <object val 1> | +----------+-----------------+ | 0x400004 | <object val 2> | +----------+-----------------+ 


在堆的内存位置之后,应该清楚地告诉你为什么你得到了你所做的输出。

现在事情变得更有趣了,因为现在你在做:

 a['two'] = 2; 

好吧,让我们一步一步来。

  • 指向包含<object val 2>内存位置0x400004
  • <object val 2>是一个空对象,因此它的符号表从空开始
  • 通过运行这一行,我们将variables'two'添加到<object val 2>的符号表中。

如果你还没有厌倦看这些图表,你会的。 事情现在看起来像这样:


全球符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400004 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

符号表<object val 1>的符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | one | 0x400004 | +--------+-----------------+ 

符号表<object val 2>的符号表

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | two | 0x400008 | +--------+-----------------+ 

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 | <object val 1> | +----------+-----------------+ | 0x400004 | <object val 2> | +----------+-----------------+ | 0x400008 | 2 (literal val) | <-- yes, even integers are stored on the heap +----------+-----------------+ in JavaScript. 


如果你花时间去跟踪记忆位置,你会看到你的浏览器显示正确的输出。

把匿名对象想象成自己的名字:

 a = {}; // The variable "a" now points to (holds) an anonymous object. b = a; // "b" points to the same anonymous object held by "a". a = 123; // "a" now holds some other value. b; // "b" still holds the anonymous object. 

关键是要记住,variables持有对对象的引用,而不是对其他variables的引用。 同一个对象可以用任意数量的variables来引用。

Javascript中的对象可以自己存在而不需要名称。 例如:

 {} 

是一个字典对象的新实例。

 a = {}; 

创build一个新的字典对象并引用它。 现在

 b = a; 

使b指向相同的基础对象。 然后你可以a别的地方说一点:

 a = "hi"; 

b仍然指向它以前所用的相同的字典对象。 b的行为与你如何改变指向无关。

就我所知,你已经使用过了,所以我猜这个引擎会把它保存在另一个内存空间中,而b仍然指向旧的内存地址(它不会被破坏)。