Javascript指针/参考疯狂.有人可以解释一下吗?

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保留了aa失去引用之前设置的值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,反之亦然.ab引用对象,可以使它们引用其他对象.像这样:

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)

  • Awww ...史诗般的失败.我发布了什么(我认为)是解释它的好方法然后我看到这个可视化...*叹气*... + 1. (6认同)
  • 有时a = {}的简单性会失去指针的清晰度.如果它有帮助,当你在脑中读到{}时,用"new object()"替换它,并记住它正在创建一个新的引用. (4认同)
  • +1真正令人惊叹和有用的解释! (3认同)

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> 是一个空对象,因此它的符号表从空开始
  • 通过运行此行,我们将变量"two"添加到<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)

.


如果您努力花时间关注内存位置,您将看到浏览器显示正确的输出.

  • @SethCarnegie,是的,但你可以在更小的空间内获得同样的观点. (4认同)
  • 哈,你的图比我的详细得多(我只是用箭头做图表).+1 (3认同)

mae*_*ics 9

将匿名对象视为具有名称:

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)

关键是要记住变量包含对象的引用,而不是对其他变量的引用.并且任何数量的变量都可以引用相同的对象.


Gre*_*ill 6

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指向的方式无关.