JavaScript - 深浅拷贝
April 12, 2025
6 min read
#前言
JS 中的数据类型可分为两种:
基本类型
:undefined
,null
,Boolean
,String
,Number
,Symbol
,BigInt
引用类型
:Object
,Array
,Date
,Function
,RegExp
等
依据MDN文档,JavaScript的数据类型分为 原始值
和 对象
,理解它们的存储和复制方式对于实现深浅拷贝至关重要。
不同类型的存储方式:
基本类型
:基本类型值在内存中占据固定大小,保存在栈内存
中引用类型
:引用类型的值是对象,保存在堆内存
中,而栈内存
中存储的是对象的变量标识符
以及对象在堆内存
中的存储地址
不同类型的复制方式:
基本类型
:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量引用类型
:从一个变量向另一个新变量复 制引用类型的值,其实复制的是储存地址,最终两个变量最终都指向同一个对象
#深浅拷贝的区别
浅拷贝
:仅仅是复制了引用,彼此之间的操作会互相影响深拷贝
:在堆中重新分配内存,不同的地址,相同的值,互不影响
#浅拷贝
依据MDN文档,对象的 浅拷贝
是属性与拷贝的源对象属性共享相同的引用(指向相同的底层值)的副本。因此,当你更改源对象或副本时,也可能导致另一个对象发 生更改。与之相比,在 深拷贝
中,源对象和副本是完全独立的。
#循环赋值
循环赋值JavaScript
function shallowClone(object) {// 只拷贝对象if (!object) return object// 依据 object 的类型判断是新建一个数组还是对象const newObject = Array.isArray(object) ? [] : {}// 遍历 object,并且判断是 object 的属性才拷贝for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] = object[key]}}return newObject}const weatherData = {condition: 'sunny',details: {temperature: 25,},}const newWeatherData = shallowClone(weatherData)newWeatherData.condition = 'CLOUDY'newWeatherData.details.temperature = 18console.log(weatherData.condition) // => sunnyconsole.log(weatherData.details.temperature) // => 18
#Object.assign
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象,它将返回目标对象。
Object.assignJavaScript
const weatherData = {condition: 'sunny',details: {temperature: 25,},}const newWeatherData = Object.assign({}, weatherData)newWeatherData.condition = 'CLOUDY'newWeatherData.details.temperature = 18console.log(weatherData.condition) // => sunnyconsole.log(weatherData.details.temperature) // => 18
#扩展运算符(...)
扩展运算符(...)JavaScript
const weatherData = {condition: 'sunny',details: {temperature: 25,},}const newWeatherData = { ...weatherData }newWeatherData.condition = 'CLOUDY'newWeatherData.details.temperature = 18console.log(weatherData.condition) // => sunnyconsole.log(weatherData.details.temperature) // => 18
#深拷贝
在 JavaScript 中, 深克隆
是指对一个对象或数组进行完整的拷贝,包括嵌套的对象和数组。深克隆的实现有多种方式,具体选择取决于数据结构的复杂程度和性能要求。
#JSON.parse(JSON.stringify(...))
这是一种简单的 深拷贝
方法,适用于不包含特殊对象的情况。
JSON.parse(JSON.stringify(object))JavaScript
const weatherData = {condition: 'sunny',details: {temperature: 25,},}const newWeatherData = JSON.parse(JSON.stringify(weatherData))newWeatherData.condition = 'CLOUDY'newWeatherData.details.temperature = 18console.log(weatherData.condition) // => sunnyconsole.log(weatherData.details.temperature) // => 25
局限性:
会忽略undefined 会忽略symbol 不能序列化函数 不能解决循环引用的对象#递归
递归
方法可以处理更复杂的对象结构,但需要考虑性能和循环引用问题。
递归JavaScript
function deepClone(obj) {if (obj === null) return objif (obj instanceof Date) return new Date(obj)if (obj instanceof RegExp) return new RegExp(obj)if (typeof obj !== 'object') return objconst cloneObj = new obj.constructor()for (let key in obj) {if (obj.hasOwnProperty(key)) {cloneObj[key] = deepClone(obj[key])}}return cloneObj}const weatherData = {condition: 'sunny',details: {temperature: 25,},}const newWeatherData = deepClone(weatherData)newWeatherData.condition = 'CLOUDY'newWeatherData.details.temperature = 18console.log(weatherData.condition) // => sunnyconsole.log(weatherData.details.temperature) // => 25
局限性:
有爆栈的风险 大对象性能问题 循环引用问题#structuredClone
全局的 structuredClone() 方法使用 结构化克隆算法
将给定的值进行 深拷贝
。这是现代浏览器提供的原生深拷贝方法,具有良好的性能和更广泛的兼容性。
structuredCloneJavaScript
const weatherData = {condition: 'sunny',details: {temperature: 25,},}const newWeatherData = structuredClone(weatherData)newWeatherData.condition = 'CLOUDY'newWeatherData.details.temperature = 18console.log(weatherData.condition) // => sunnyconsole.log(weatherData.details.temperature) // => 25
优点:
性能比递归实现更好 可以处理循环引用 支持更多的内置类型局限性:
不能克隆函数、DOM节点和部分内置对象 兼容性仍在改善中,较新的 API 可能不支持