问题背景
我之前一直以为浅拷贝出来的新对象和旧对象的引用地址是相同的,但是通过Object和===
发现浅拷贝的新对象和旧对象的引用地址不同!!
const obj1 = { name: "Alice", test: { age: 12 } };const obj4 = Object.assign({}, obj1); // 浅拷贝obj4.name = "hhh";obj4.test.age = 99;console.log("obj1", obj1);console.log("obj4", obj4);const obj2 = { name: "Alice" };const obj3 = obj1;console.log(Object.is(obj1, obj2)); // false,因为它们的引用地址不同console.log(Object.is(obj1, obj3)); // true,因为它们指向同一个对象console.log(Object.is(obj1, obj4)); // false!!!console.log(obj1 === obj2); // falseconsole.log(obj1 === obj3); // trueconsole.log(obj1 === obj4); // false!!!
甚至当对象不止一层的时候,也是引用对象不同
因为我一直以为不止一层,那么浅拷贝就直接拷贝地址,那么引用地址总应该相同了吧,唉
const obj5 = {test: { age: 12 } };const obj6 = Object.assign({}, obj5); // 浅拷贝console.log(Object.is(obj5, obj6)); // false!!!console.log(obj5 === obj6); // false!!!
还是有疑问
既然浅拷贝出来的对象引用地址不同,为什么obj1.test.age也修改了
const obj1 = { name: "Alice", test: { age: 12 } };const obj4 = Object.assign({}, obj1);obj4.name = "hhh";obj4.test.age = 99;console.log("obj1", obj1);console.log("obj4", obj4);
这是因为浅拷贝只会复制对象的第一层属性,而不会递归复制对象中嵌套的对象。所以虽然 obj1 和 obj4 是两个不同的对象,但它们的 test 属性引用的是同一个对象。
console.log(Object.is(obj1.test, obj4.test)); // true!!!console.log(obj1.test === obj4.test); // true!!!
修改 obj4.test.age 实际上修改了 test 属性指向的对象,因此 obj1 中的 test 属性也会受到影响。
要解决这个问题,你需要使用深拷贝而不是浅拷贝。深拷贝会递归地复制对象及其所有嵌套的对象,确保所有对象都是独立的,没有共享引用。
在插一句
const obj7 = { name: "Alice" };const obj8 = Object.assign({}, obj7); // 浅拷贝console.log(Object.is(obj7.name, obj8.name)); // trueconsole.log(obj7.name === obj8.name); // true
在这段代码中,obj7 和 obj8 中的 name 属性都是字符串,它们的值是相同的,因此 Object.is(obj7.name, obj8.name) 和 obj7.name === obj8.name 都会返回 true。
需要注意的是,这里的比较并不是比较两个对象的引用地址,而是比较它们的值。在 JavaScript 中,字符串是基本数据类型,比较时会直接比较它们的值。
要判断两个字符串的引用是否相同,可以使用严格相等运算符(===)或 Object.is() 方法进行比较。这会比较字符串的引用地址是否完全相同。
const str1 = "hello";
const str2 = "hello";
const str3 = new String("hello");console.log(str1 === str2); // true,直接量相同
console.log(str1 === str3); // false,str3 是一个对象,与 str1 的引用不同
console.log(Object.is(str1, str2)); // true
console.log(Object.is(str1, str3)); // false
在这个例子中,str1
和 str2
是直接量,它们的引用地址相同,因此严格相等运算符和 Object.is()
方法都会返回 true
。而 str3
是通过构造函数 String
创建的字符串对象,与直接量的引用地址不同,因此比较结果为 false
。
总结
浅拷贝出来的对象和旧对象的引用地址是不同的。在浅拷贝过程中,会创建一个新的对象,该对象包含了源对象的所有可枚举属性的副本。因此,尽管浅拷贝后的对象可能与源对象具有相同的属性值,但它们是两个不同的对象,存储在内存中的不同位置,拥有不同的引用地址。
引用别人的文章:
http://t.csdnimg.cn/zSoTn
let arrayA = [1, '这是一个字符串', undefined, null, { obj: '这是一个obj' }];
console.log('原始的arrayA', arrayA)//原始的arrayA [1, 2, 3, 4, 5, 6, 7]
let arrayB = Array.from(arrayA)//浅拷贝(注意,浅拷贝只会拷贝一层)
let arrayC = arrayA//应用arrayA的堆地址arrayA[0] = 99
arrayA[1] = "修改一下这个字符串"
arrayA[2] = "修改一下这个undefind"
arrayA[3] = "修改一下这个null"
arrayA[4].obj = "修改一下这个obj"console.log('arrayA', arrayA)//[99, '修改一下这个字符串', 修改一下这个undefind, 修改一下这个null, { obj: '修改一下这个obj' }];
//arrayB是由Array.form浅拷贝来的,
console.log('arrayB', arrayB)//[1, '这是一个字符串', undefined, null, { obj: '修改一下这个obj' }]
//arrayC地址应用(arrayC是指向arrayA这个数组对象的堆内存的内存地址),所以arrayA改变后arrayC也会改变,因为它两指向的是同一个内存地址;
console.log('arrayC', arrayC)//[99, '修改一下这个字符串', 修改一下这个undefind, 修改一下这个null, { obj: '修改一下这个obj' }];