【问题标题】:Why does my JS object incorrectly change when not assigning the object to a variable?为什么我的 JS 对象在不将对象分配给变量时会错误地更改?
【发布时间】:2021-07-31 20:21:22
【问题描述】:

请给我解释一下这个现象。

this.testObject = { a: 1, b: { a: 3, b: { a: 78, b: null } } };

let g = this.testObject;

while (g.b) {
  g = g.b;
}

g.b = {
  a: 79,
  b: null
};

console.log(this.testObject); // { a: 1, b: { a: 3, b: { a: 78, b: { a: 79, b: null } } } }

现在我决定坚持使用this.testObject 并删除g

this.testObject = { a: 1, b: { a: 3, b: { a: 78, b: null } } };

while (this.testObject.b) {
  this.testObject = this.testObject.b;
}

this.testObject.b = {
  a: 79,
  b: null
};
console.log(this.testObject); // {a": 78,b": {a": 79,b": null}

发生了什么事?我分配的g 是如何影响这个对象的?

在第一个 sn-p 中,如果我做了 g=g.b,那么我基本上是在为 g 分配一个新值。但是,如果我明确地这样做,那么结果就不一样了?

this.testObject = {a:1, b:{a:3,b:{a:78, b:null}}};
let g = this.testObject;

while(g.b){
    g = {a:7777, b:null};
}

g.b={a:79, b:null};
this.testObject; // {a": 1,b": {a": 3,b": {a": 78,b": null}

【问题讨论】:

  • ??您的循环明显更改 this.testObject 为一系列新值。没有任何东西可以将其恢复到原来的值。
  • 预期的行为是什么?
  • gthis.testObject 是两个独立的可修改的东西(g 是一个变量,this.testObject 是一个对象属性)。更改 g 不会更改 this.testObject 的值。
  • @TrickOrTreat g不是副本。这只是一个参考。重新分配 g 不会影响之前分配的引用。

标签: javascript linked-list javascript-objects


【解决方案1】:

通常在回答这些问题时,人们会在它们之间画出方框和箭头来表示指针,但我很懒,所以让我们考虑一个简单的虚拟机。这台机器可以对内存块及其编号(又名“指针”)进行操作。共有三种操作:

  • allocate:分配一个内存块,用一些数据初始化它并返回一个指向它的指针(表示为$number
  • 写:将变量或属性的值设置为指针,使变量“指向”对应的内存块
  • 读取:给定一个指针,返回其内存块的内容

就这台机器而言,您的第一个 sn-p 将转换为:

// this.testObject = { a: 1, b: { a: 3, b: { a: 78, b: null } } };

allocate: $1 = {a: 78, b: null}
allocate: $2 = {a: 3, b: $1}
allocate: $3 = {a: 1, b: $2}

write: this.testObject = $3

// let g = this.testObject;

write: g = $3

// while (g.b) {  g = g.b; }

write: g = $3.b // first pass, g is now $2
write: g = $2.b // second pass, g is now $1

// g.b = { a: 79, b: null }

allocate: $4 = { a: 79, b: null }

write: $1.b = $4

// console.log(this.testObject);

read: $3 ==> {a: 1, b: $2}
read: $2 ==> {a: 3, b: $1}
read: $1 ==> {a: 78, b: $4}
read: $4 ==> { a: 79, b: null }

基本上,每个{...} 都是一个“分配”操作,每个赋值都是一个“写”,console.logs 是一个“读”。尝试将您的其他 sn-ps“翻译”到这台机器上,您会看到实际发生了什么。

【讨论】:

    【解决方案2】:

    我猜你希望这两个 sn-ps 产生相同的结果。但是代码不会做同样的事情。

    您在第一个示例中使用g 作为一种参考。这意味着每次在 while 循环中设置 g 时,您都不会以任何方式影响 this.testObject,因为您只是在“移动”引用。但它仍然是一个引用,因此更改 g 引用的对象内部的任何内容,您还将在 this.testObject 中设置它,从而产生您在第一个示例中得到的结果。

    在第二个示例中,您每次都在 while 循环中替换 this.testObject。所以设置this.testObject = this.testObject.b 将删除外部对象,导致this.testObject 来自:

    { a: 1, b: { a: 3, b: { a: 78, b: null } } }
    

    收件人:

    { a: 3, b: { a: 78, b: null } }
    

    并且由于this.testObject.b 不为空,因此您再次重复此操作,使this.testObject 变为:

    { a: 78, b: null }
    

    最后,您将this.testObject.b 设置为一个新对象,覆盖之前的空值。这将导致您在第二个示例中获得的输出。

    【讨论】:

    • 我刚刚在 javascript 代码中更新了我的恐惧。请看一看。
    • 但是为什么 g = {a:7777, b:null}; 的工作方式不同(因为它没有考虑 g.b={a:79, b:null}; )而是 g=g.b 。能够包含g.b={a:79, b:null};
    • @TrickOrTreat 每次编写 g = ... 时,您都在重新分配 g 所引用的内容。在您的新代码示例中,您首先将g 指定为指向this.testObject,然后用新的{a:7777, b:null}; 覆盖该引用。由于您正在移动引用,因此您不会以任何方式更改 this.testObject。我建议查看 this article,它解释了 javascript 中值和引用之间的区别。
    • 哦.好的。感谢您的解释。这有帮助。
    猜你喜欢
    • 1970-01-01
    • 2014-08-26
    • 2016-08-26
    • 2016-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-08
    相关资源
    最近更新 更多