Phi*_*ton 63 javascript pointers reference
Javascript通过引用传递对象.这很有道理.但是一旦你开始操纵这些物体,一切都会以一种看起来不直观的方式发挥作用.让我举一个例子:
var a, b;
a = {}
b = a;
a['one'] = {};
console.log( JSON.stringify(a) );
// outputs: {"one":{}}
console.log( JSON.stringify(b) );
// outputs: {"one":{}}
Run Code Online (Sandbox Code Playgroud)
这一切都很好,因为现在b有一个指针,a所以预计分配东西a也会影响b.
但是如果我这样做:
a = a['one'];
console.log( JSON.stringify(a) );
// outputs: {}
console.log( JSON.stringify(b) );
// outputs: {"one":{}}
Run Code Online (Sandbox Code Playgroud)
这对我来说很惊讶.我期望a,并b仍然是相同的(并且是{}因为a['one']原先设置为{}和a设置为a['one']).
但事实并非如此.似乎a失去了b对它被赋予新内容的时间的引用,但b保留了a在a失去引用之前设置的值b.
但是如果我这样做:
a['two'] = 2;
console.log( JSON.stringify(a) );
// outputs: {"two":2}
console.log( JSON.stringify(b) );
// outputs: {"one":{"two":2}}
Run Code Online (Sandbox Code Playgroud)
什么?a已经明显失去了它的参考b,但b似乎仍然有一些参考a.
空对象是否{}指向内存中的某个位置,因此引用它的每个变量现在都指向同一个位置?
能够牢牢掌握这一点的人能解释一下吗?
Set*_*gie 203
按行逐个示例:
a = {}
Run Code Online (Sandbox Code Playgroud)
a 现在引用新对象.
b = a;
Run Code Online (Sandbox Code Playgroud)
b现在引用引用的相同对象a.请注意,它没有引用a.
a['one'] = {};
Run Code Online (Sandbox Code Playgroud)
新对象现在有一个'one'引用另一个新对象的索引.
当你这样做
a = a['one'];
Run Code Online (Sandbox Code Playgroud)
您正在设置a引用a['one'],这是您创建时创建的新对象a['one'] = {}.b仍引用您创建的对象a = {}.
当你说" a已经失去了对它的引用b" 时你会混淆这个问题,因为a它没有引用b,反之亦然.a并b引用对象,可以使它们引用其他对象.像这样:
有a = {}; b = a,你得到
a
\
\
{ }
/
/
b
Run Code Online (Sandbox Code Playgroud)
然后a['one'] = {}你得到
a
\
\
{ one: { } }
/
/
b
Run Code Online (Sandbox Code Playgroud)
然后a = a['one']你得到
a - - - -
\
{ one: { } }
/
/
b
Run Code Online (Sandbox Code Playgroud)
riw*_*alk 45
:P你正在深入细致的细节,我很高兴你问,因为到最后你会更聪明.
不要用指针来看待它,因为我认为这是你感到困惑的地方.可以考虑它而不是堆(或者只是"内存",如果你愿意)和符号表.
让我们从代码的前几行开始:
var a, b;
a = {}
b = a;
Run Code Online (Sandbox Code Playgroud)
你在这里做的是在堆上创建一个对象,在符号表上创建两个符号.它看起来像这样:
符号表:
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400000 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
堆:
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
Run Code Online (Sandbox Code Playgroud)
.
事情变得有趣:对象有自己的"符号表"(通常这些只是哈希表,但称之为符号表可以使它更清晰).
现在,在下一个语句之后,您需要考虑三件事:全局符号表,<object val 1>符号表和堆.
运行以下行:
a['one'] = {}
Run Code Online (Sandbox Code Playgroud)
现在事情看起来像这样:
全球符号表:
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400000 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
<object val 1>符号表
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| one | 0x400004 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
堆:
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
| 0x400004 | <object val 2> | <---we created a new object on the heap
+----------+-----------------+
Run Code Online (Sandbox Code Playgroud)
.
现在您运行以下代码:
a = a['one'];
Run Code Online (Sandbox Code Playgroud)
希望这似乎是一个微不足道的变化.结果是:
全球符号表:
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400004 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
<object val 1>符号表
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| one | 0x400004 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
堆:
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
| 0x400004 | <object val 2> |
+----------+-----------------+
Run Code Online (Sandbox Code Playgroud)
.
跟随堆的内存位置应该有希望清楚为什么你得到你做的输出.
现在事情变得更有趣了,因为现在你正在做:
a['two'] = 2;
Run Code Online (Sandbox Code Playgroud)
好的,让我们一步一步来.
a指向0x400004包含的内存位置<object val 2><object val 2> 是一个空对象,因此它的符号表从空开始<object val 2>符号表中.如果你还没有厌倦看这些图表,你会是.事情现在看起来像这样:
全球符号表:
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400004 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
<object val 1>符号表
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| one | 0x400004 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
<object val 2>符号表
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| two | 0x400008 |
+--------+-----------------+
Run Code Online (Sandbox Code Playgroud)
堆:
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
| 0x400004 | <object val 2> |
+----------+-----------------+
| 0x400008 | 2 (literal val) | <-- yes, even integers are stored on the heap
+----------+-----------------+ in JavaScript.
Run Code Online (Sandbox Code Playgroud)
.
如果您努力花时间关注内存位置,您将看到浏览器显示正确的输出.
将匿名对象视为具有名称:
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.
Run Code Online (Sandbox Code Playgroud)
关键是要记住变量包含对象的引用,而不是对其他变量的引用.并且任何数量的变量都可以引用相同的对象.
Javascript中的对象可以自己存在而无需名称.例如:
{}
Run Code Online (Sandbox Code Playgroud)
是字典对象的新实例.
a = {};
Run Code Online (Sandbox Code Playgroud)
创建一个新的字典对象并a引用它.现在
b = a;
Run Code Online (Sandbox Code Playgroud)
使得b引用相同的底层对象.然后你可以在a其他地方指出:
a = "hi";
Run Code Online (Sandbox Code Playgroud)
并且b仍然指向它之前所做的相同字典对象.行为b与你改变a指向的方式无关.