【问题标题】:Two objects which have the same property. What is the effect of deleting the property of one object?具有相同属性的两个对象。删除一个对象的属性有什么影响?
【发布时间】:2017-12-19 07:26:20
【问题描述】:

我对这两种情况感到困惑:

第一个:

let a: object = {'name': 'gavin'};
let b: object = {};
b['name'] = a['name']; // at this time. a and b are both {'name': 'gavin'};
delete a['name']; // now a is {} and b is {'name': 'gavin'}

第二个:

let a: object = {'tag': {'name': 'gavin'}};
let b: object = {};
b['tag'] = a['tag']; // at this time. a and b are both {'tag': {'name': 'gavin'}};
delete a['tag']['name']; // now a and b are both {'tag': {}}

在第二种情况下,如果我delete a['tag'],a 将是{},b 将是{'tag': {'name': 'gavin'}}。正如我所料,如果我删除a['tag'],b 将为空,如果我删除a['tag']['name'],b 不会受到影响。为什么会出现这样的结果?

英语不是我的母语,如果我有拼写错误,请随时告诉我。

【问题讨论】:

  • a.tagb.tag 共享对对象 {'name': 'gavin'} 的相同引用。所以,delete-ing 一个正在修改另一个。
  • 因为delete a.tag.name 根本不影响a(或b),它确实会影响从a.tagb.tag 引用的对象。

标签: javascript


【解决方案1】:

在您的第一个示例中,您分配了一个字符串b['name'] = a['name']。字符串在 javascript 中按值传递,因此有效地在内存中创建了该字符串的新副本。 a['name'] 不等于 b['name'] 在它们引用的实际内存位置方面。

您可以将其视为具有两个字符串副本。当你删除字符串时,你删除了一个副本,而不是另一个。

在您的第二个示例中,您正在分配一个对象b['tag'] = a['tag']。对象在 javascript 中通过引用传递,这意味着您不是创建对象的新副本,而是将相同的对象分配给 b['tag']。所以a['name']b['name'] 是同一个对象。

您可以将其视为拥有一个对象的一个​​副本,其中a['tag']b['tag'] 都使用该单个副本。当您从该单个副本中删除 name 属性时,它不会出现在 a['tag']b['tag'] 中,因为它们都使用同一个对象。

这个问题的答案可能会帮助您更好地理解价值与对象引用https://stackoverflow.com/a/37290849/845704

编辑

也许从透视角度来看,让我们将您的示例与附加变量一起使用。请记住,这在数据结构方面与您的示例没有什么不同,只是多了几行代码。

let tag_obj = {'name': 'gavin'};
let a = { 'tag': tag_obj };
let b = {};
b['tag'] = a['tag'];

// a and b both now equal { tag: { name: 'gavin'} }

console.log(a.tag === tag_object) // True, it references tag object
console.log(b.tag === tag_object) // Also true, as it references the same object.

delete a.tag.name; 

现在,如果您 delete a.tag,您正在从 a 对象中删除一个属性。这不应该影响其他任何东西,因为a 不等于其他任何东西。但是,当您delete a.tag.name 时,您正在从tag 对象中删除一个属性,我们在上面的示例中将其称为tag_object。由于a.tagb.tag 都引用了tag_object,它们现在都将显示为没有名称属性。


没有本地“操作符”来制作对象的额外副本,但有一些常用的方法。第一个是Object.assign,它将一个对象的属性分配给另一个对象

let j = {'tag': {'name': 'james'}};
let k = j
console.log(k === j) // Will log true, they are the same object

let a = {'tag': {'name': 'gavin'}};
let b = Object.assign({}, a);
console.log(a === b) // Will log false, a new object has been created.

现在两个对象看起来都一样了。不幸的是,Object.assign 创建了所谓的浅拷贝。所以对象本身是不同的,但是对象内部的任何引用都会被共享

let a = {'tag': {'name': 'gavin'}};
let b = Object.assign({}, a);
console.log(a.tag === b.tag) // Will log true, they share the same tag object

周围有一些实用程序库可以为您创建对象的深层副本。您也可以通过遍历整个对象树以递归方式自己执行此操作。

但是,创建对象的深层副本的一种常见且相当快速的方法是使用 JSON。

let j = {'tag': {'name': 'james'}};
let k = JSON.parse(JSON.stringify(j));
console.log(j === k); // Will log false, a new object has been created.
console.log(j.tag === k.tag); // Will log false, a new object has been created.

此机制首先使用JSON.stringify 生成一个新字符串,此时它不再与源对象绑定。然后,它使用 JSON.parse 从该字符串生成一个全新的对象。

注意

如果您使用 JSON 方法,则不会保留函数,因为函数不能以任何标准 JSON 格式序列化。

【讨论】:

  • 但如果我删除 a['tag'],a 将是 {},b 将是 {'tag': {'name': 'gavin'}}。为什么 b 不改成{}
  • @gavingSong 因为delete 操作数作用于内部{'name': 'gavin'} 对象,父母双方都指向该对象。
  • delete 对 JavaScript 中的许多人来说是一个令人困惑的词,因为它并没有真正删除任何东西,所有 delete 所做的只是从对象中删除引用。从内存中删除实际数据只发生在垃圾收集阶段,无法在代码中控制。如果您执行delete a,您会注意到a 仍然存在,因为您无法控制实际数据的删除。 delete a['tag'] 只是说“仅删除对该对象的单个副本的引用”。
  • @gavinSong 您可能想多了解一下对象引用与对象值。有很多资源可以描述和解释它,可能比我做得更好。它不是与特定语言相关的概念,它与数据在内存中的存储方式有关。
猜你喜欢
  • 1970-01-01
  • 2018-02-17
  • 1970-01-01
  • 2017-02-27
  • 1970-01-01
  • 2017-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多