一文读懂:JavaScript对象拷贝的奥秘
前言
在JavaScript中有一个非常神奇的方法叫作拷贝(copy),通常拷贝只针对引用类型,然而拷贝被分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)两种方式。这两种拷贝方式的区别在于如何处理对象中的引用类型。在本文中Virtual09将会为大家介绍这两种拷贝方式.
正文
浅拷贝
浅拷贝会复制对象的第一层属性,但对于对象中的引用类型(如数组、对象等),它只会复制这些引用类型的引用,而不是其实际的内容。这意味着如果原始对象和拷贝对象中的引用类型指向同一个内存地址,那么对其中一个对象的修改会影响到另一个对象。 现在让我们来看一个简单的例子
let obj = {
age: 18
}
let obj2 = obj
obj.age = 20
console.log(obj2.age);
我们把obj
赋值给了obj2
,但是这两个的对象的引用地址是指向同一个引用地址的,所以当我们修改数据obj.age=20
时,对于obj2.age
的值也会被修改成20,反过来修改obj2.age=25
,obj.age
的值也会是25.
这可以说是一种非常基础的浅拷贝,那么浅拷贝还有别的方法吗,答案是当然的兄弟,肯定存在其他方法,话不多说我们来看看其他的方法.
Object.create(x)
Object.create(x)
这个方法会创建一个新的对象,将对象x
作为其创建的新对象的原型。这也是一种浅拷贝,你也可以把它看做一种继承,严格来说是原型式继承
。
let a = {
name: '毛毛'
}
let b = Object.create(a)
a.name = '付哥'
console.log(b.name);
它的执行机制是这样的,创建的新对象b={}
其实是这样的,它的原型b,prototype = a
,我们修改了a.name
的值,现在我们去打印b.name
,这时候V8就会去对象b
上面找到一个叫做name
的元素,但是找不到,就会通过原型链去b
的原型a
上面找一个属性为name
.找到然后把它输出。
Object.assign({},obj)
这个方法是将源对象obj
中所有可枚举的属性,复制到新的目标对象中,但是也是浅拷贝的一种方式。
但是如果枚举的属性是原始类型是不会触发浅拷贝了,如果是引用类型,就会把地址给引用过来
let a = {
name: '毛毛',
like: {
n: 'running'
}
}
let c = Object.assign({}, a)
a.name = '徐总'
a.like.n = 'swimming'
console.log(c);
[].concat(arr)
数组拼接,我们把源数组拼接到新的数组中。这种方法也是,如果数组中的元素的引用类型,那么复制的也只是引用。只有我们修改了源数组中的引用类型,新数组中的值才会跟着被修改。
let arr = [1, 2, 3, {a: 10}]
let newArr = [].concat(arr)
arr[3].a = 2
console.log(newArr);
console.log(arr);
let arr = [1, 2, 3, {a: 10}]
let newArr = [].concat(arr)
arr[0] = 6
console.log(newArr);
console.log(arr);
数组解构
数组解构也可以用来实现数组的浅拷贝。这会创建一个新数组,并将原始数组的元素复制到新数组中。同样,如果元素是引用类型,则复制的只是引用。
let arr = [1, 2, 3, {a: 10}]
let newArr = [...arr]
arr.slice()
slice
方法会返回一个从开始到结束(不包括结束位置)的数组的浅拷贝。这是一个常用的浅拷贝数组的方法。
let arr = [1, 2, 3, {a: 10}]
let newArr = arr.slice(0)
深拷贝
 深拷贝会递归地复制对象的所有层级,包括所有的引用类型属性,确保拷贝的对象和原始对象之间没有共享的引用。这意味着修改拷贝对象不会影响到原始对象,反之亦然。
JSON.parse(JSON.stringify(obj))
我们利用JSON的序列化和解析过程来生成一个深拷贝,首先JSON.stringify(obj)会将对象obj
转化成为一个JSON字符串,在这个转化过程中,会遍历该对象的整个结构,然后,JSON.parse()会将这个JSON字符串转换成一个JavaScript对象,且这个对象的全新的和obj
没有任何关系。
let obj = {
name: '萍萍',
age: 18,
like: {
n: 'coding'
},
a: true,
b: undefined,
c: null,
d: Symbol(1),
f: function() {}
}
let obj2 = JSON.parse(JSON.stringify(obj))
obj.like.n = 'running'
console.log(obj2);
当然这个方法不支持函数、正则表达式、Date对象、Error对象、Symbol、WeakMap/WeakSet等非标准JSON类型。
如果对象中存在循环引用,这个方法将会抛出错误,因为JSON.stringify()
遇到循环引用时会失败。
structuredClone
structuredClone()
是一个在浏览器环境下可用的API,用于克隆对象和数组,包括所有子级的引用类型。这个方法能够正确处理循环引用和各种数据类型,包括Blob
、File
、ImageBitmap
等。它- 支持更广泛的数据类型,包括一些非标准JSON类型。
const user = {
name: {
firstName: '牛',
lastName: '蜗'
},
age: 18
}
const newUser = structuredClone(user)
user.name.firstName = '牛牛'
console.log(newUser);
本文到此结束了,希望文章对各位大佬有用,若有不足,恳请指出!谢谢大家!
转载自:https://juejin.cn/post/7393481473083637801