在您的第一个示例中,您分配了一个字符串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.tag 和b.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 格式序列化。