Javascript 指针/引用疯狂。有人可以解释一下吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8318357/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:32:55  来源:igfitidea点击:

Javascript pointer/reference craziness. Can someone explain this?

javascriptpointersreference

提问by Philip Walton

Javascript passes objects by reference. This makes perfect sense. But once you start manipulating those objects, everything acts in a way that seem unintuitive. Let me offer an example:

Javascript 通过引用传递对象。这是完全有道理的。但是一旦你开始操纵这些对象,一切都会以一种看起来不直观的方式运行。让我举个例子:

var a, b;

a = {}
b = a;
a['one'] = {};

console.log( JSON.stringify(a) );
// outputs: {"one":{}}

console.log( JSON.stringify(b) );
// outputs: {"one":{}}

This is all well and good because now bhas a pointer to aso it's expected that assigning stuff to awill also affect b.

这是一切都很好,因为现在b有一个指针,a所以它的预期,到指定的东西a也会影响b

But then if I do this:

但是如果我这样做:

a = a['one'];

console.log( JSON.stringify(a) );
// outputs: {}

console.log( JSON.stringify(b) );
// outputs: {"one":{}}

This is surprising to me. I'd expect aand bto still be the same (and to be {}since a['one']was previously set to {}and awas set to a['one']).

这让我很惊讶。我希望a并且b仍然是相同的(并且{}因为a['one']之前被设置为{}并且a被设置为a['one'])。

But that's not the case. It appears that aloses its reference to bwhen it's assigned to something new, but bmaintains the value that awas set to prior to aloosing its reference to b.

但事实并非如此。它似乎a失去了对b何时分配给新事物的引用,但b保持了aa失去对b.

But then if I do this:

但是如果我这样做:

a['two'] = 2;

console.log( JSON.stringify(a) );
// outputs: {"two":2}

console.log( JSON.stringify(b) );
// outputs: {"one":{"two":2}}

What? ahas clearly lost it's reference to b, but bseems to still have some reference to a.

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

Does the empty object {}point to some place in memory so every variable referencing it is now pointing to the same place?

空对象是否{}指向内存中的某个位置,因此引用它的每个变量现在都指向同一个位置?

Can someone with a firm grasp on this explain it to me?

对此有深入了解的人可以向我解释一下吗?

回答by Seth Carnegie

Following your example line by line:

逐行按照您的示例进行操作:

a = {}

anow references the new object.

a现在引用新对象。

b = a;

bnow references the same object that areferences. Note that it does not reference a.

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

a['one'] = {};

The new object now has an index 'one'that references another new object.

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

When you do

当你做

a = a['one'];

You are setting ato refer to a['one'], which is that new object you created when you did a['one'] = {}. bstill references the object you created with a = {}.

您正在设置a为引用a['one'],即您在创建时创建的新对象a['one'] = {}b仍然引用您创建的对象a = {}

You are confusing the issue when you say "ahas lost its reference to b" because adoes not refer to b, nor vice versa. aand brefer to objects, and they can be made to refer to other objects. Like this:

当你说“a已经失去了对b”的引用时,你会混淆这个问题,因为a不引用b,反之亦然。ab引用对象,它们可以被用来引用其他对象。像这样:

With a = {}; b = a, you get

随着a = {}; b = a,你得到

a
 \
  \
   { }
  /
 /
b

Then with a['one'] = {}you get

然后和a['one'] = {}你一起得到

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

Then with a = a['one']you get

然后和a = a['one']你一起得到

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

回答by riwalk

:P You're descending into the knitty gritty details and I'm glad you asked, as you will be wiser by the end.

:P 你正在深入了解细节,我很高兴你问了,因为到最后你会更聪明。

Don't look at it in terms of pointers, because I think that is where you are getting confused. Think of it rather in terms of the heap (or just "memory" if you will) and the symbol table.

不要用指针来看待它,因为我认为这就是你感到困惑的地方。而是从堆(或者只是“内存”,如果你愿意的话)和符号表的角度来考虑它。

Lets start by taking the first few lines of your code:

让我们从代码的前几行开始:

var a, b;

a = {}
b = a;

What you've done here is created one object on the heap and two symbols on the symbol table. It looks something like this:

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



Symbol Table:

符号表

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

Heap:

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

.

.



Here's where things get interesting: Objects have their own "symbol tables" (usually these are just hash tables, but calling it a symbol table can make it clearer).

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

Now, after your next statement, you have 3 things to consider: The global symbol table, <object val 1>'s symbol table, and the heap.

现在,在您的下一条语句之后,您需要考虑 3 件事:全局符号表、<object val 1>的符号表和堆。

Run the following line:

运行以下行:

a['one'] = {}

And now things look like this:

现在事情看起来像这样:



Global Symbol Table:

全局符号表

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

<object val 1>'s Symbol Table

<object val 1>的符号表

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

Heap:

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

.

.



Now you ran the following code:

现在您运行了以下代码:

a = a['one'];

This should hopefully seem to be a trivial change. The result is:

希望这看起来是一个微不足道的变化。结果是:



Global Symbol Table:

全局符号表

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

<object val 1>'s Symbol Table

<object val 1>的符号表

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

Heap:

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

.

.



Following the memory locations to the heap should hopefully make it clear why you got the output you did.

沿着内存位置到堆应该可以清楚地说明为什么你得到了你所做的输出。

Now things get even MORE interesting, because now you are doing:

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

a['two'] = 2;

Ok, so let's take this step by step.

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

  • apoints to memory location 0x400004which contains <object val 2>
  • <object val 2>is an empty object, thus its symbol table starts off empty
  • By running this line, we add the variable 'two' to <object val 2>'s symbol table.
  • a指向0x400004包含的内存位置<object val 2>
  • <object val 2>是一个空对象,因此它的符号表开始是空的
  • 通过运行这一行,我们将变量 'two' 添加到<object val 2>'s 符号表中。

If you're not tired of looking at these diagrams yet, you will be. Things now look like this:

如果您还没有厌倦查看这些图表,那么您一定会厌倦的。事情现在看起来像这样:



Global Symbol Table:

全局符号表

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

<object val 1>'s Symbol Table

<object val 1>的符号表

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

<object val 2>'s Symbol Table

<object val 2>的符号表

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

Heap:

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

.

.



If you diligently take the time to follow the memory locations, you will see that your browser displayed the correct output.

如果您认真地花时间跟踪内存位置,您将看到您的浏览器显示了正确的输出。

回答by maerics

Think of the anonymous object as itself having a name:

把匿名对象想象成它自己有一个名字:

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.

The key is to remember that variables hold references to objects, not references to other variables. And the same object may be referred to by any number of variables.

关键是要记住变量保存对对象的引用,而不是对其他变量的引用。并且同一个对象可以被任意数量的变量引用。

回答by Greg Hewgill

Objects in Javascript can exist by themselves without needing a name. For example:

Javascript 中的对象可以独立存在而无需名称。例如:

{}

is a new instance of a dictionary object.

是字典对象的新实例。

a = {};

creates a new dictionary object and makes arefer to it. Now

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

b = a;

makes brefer to the same underlying object. You can then make apoint somewhere else:

使b引用相同的底层对象。然后你可以在a其他地方指出:

a = "hi";

and bstill points to the same dictionary object it did before. The behaviour of bis unrelated to how you change what apoints to.

并且b仍然指向它之前所做的同一个字典对象。的行为b与您如何更改a指向的内容无关。

回答by R01010010

As far as i know you overwrited aso i guess the engine saves it in another memory space, whereas bstill pointing to the old a's memory address (which somehow doesn't get destroyed).

据我所知你覆盖了 a所以我猜引擎将它保存在另一个内存空间中,而b仍然指向旧的a的内存地址(不知何故不会被破坏)。