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":{}}
这对我来说是令人惊讶的。 我希望a
和b
仍然是相同的(因为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
,反之亦然。 a
和b
是指对象 ,可以用来引用其他对象。 喜欢这个:
用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仍然指向旧的内存地址(它不会被破坏)。