JavaScript中的深拷贝和浅拷贝常用方法总结

Posted cwxblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript中的深拷贝和浅拷贝常用方法总结相关的知识,希望对你有一定的参考价值。

本文乃学习总结,学习参考自:https://medium.com/javascript-in-plain-english/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089

对于引用类型(数值类型的拷贝就是字面的拷贝,就不说了)来说,赋值运算符 "=" 进行的拷贝是对引用的拷贝(实质上用的是同一个引用),其中一个值的改变,会导致另一个值随之改变。但有时我们并不想这样做,这就可以通过如下深拷贝和浅拷贝(两者复制的都是不同的引用)实现。以下介绍几种浅拷贝和深拷贝常用的几种方法。

let array = [1,2,3]; 
let copyArray = array; 
console.log(...array) // 1 2 3 
array[0] = 99; 
console.log(...array) // 99 2 3 
console.log(...copyArray) // 99 2 3

1. 浅拷贝

浅拷贝只适用没有嵌套的数组或对象,对于嵌套的数组或对象,浅拷贝将会失效。

// 1. 扩展运算符 ... 适合没有嵌套的数组和对象进行浅拷贝
const array = ['A','B','c']
const copyWithSpread = [...array] // 改变array 不会改变 copyWithSpread

// 2. Array.slice(start,length)方法,适合数组的浅拷贝
const copyWithSlice = array.slice() // 改变 array 不会改变 copyWithSlice

// 3. Object.assign() 可用于任何对象or数组的浅拷贝
const copyWithAssign = [] // Changes to array will not change copyWithAssign
Object.assign(copyWithAssign, array) // Object.assign(target, source)

// 4. Array.from() ,适用于数组的浅拷贝
const copyWithFrom = array.from()

1.1 扩展运算符的深层嵌套问题

如果对象或数组包含其他对象或数组,则浅拷贝将无法正常工作,因为嵌套对象实际上并未克隆。

// 当包含数组的javascript对象深度嵌套时,
// 扩展运算符仅复制的第一层的引用,但更深的值仍链接在一起。所以会造成深拷贝无效。
const nestedArray = [[1,2],['a','b'],[3]];
const nestedCopyWithSpread = [...nestedArray];
console.log(nestedCopyWithSpread); // [[1,2],['a','b'],[3]]

nestedArray[0][0] = 'hello';
console.log(nestedArray); // [["hello",2],['a','b'],[3]]
console.log(nestedCopyWithSpread); // [["hello",2],['a','b'],[3]]

1.2 深拷贝

适用嵌套多层对象和数组的对象or 数组

lodash: https://lodash.com/docs/#cloneDeep

radma: https://ramda.cn/docs/#clone

rfdc: https://www.npmjs.com/package/rfdc

// 1. lodash库,深拷贝常用方法,简单易用
import _ from "lodash"
const nestedArray = [[1,2],['a','b'],[3]];
const shallowCopyWithLodashClone = _.clone(nestedArray) 
console.log(nestedArray[0] === shallowCopyWithLodashClone[0]) // true -- 浅拷贝(相同引用)

const deepCopyWithLodashCloneDeep = _.cloneDeep(nestedArray)
console.log(nestedArray[0] === deepCopyWithLodashCloneDeep[0]) // false -- 深拷贝 (不同引用)
nestedArray[0][0] = 'hello';
console.log(...nestedArray) // [["hello",2],['a','b'],[3]]
console.log(...shallowCopyWithLodashClone) // [["hello",2],['a','b'],[3]]
console.log(...deepCopyWithLodashCloneDeep) // [[1,2],['a','b'],[3]]

// 2. ramda库 
import R from "ramda" ;
const deepCopyWithRamdaClone = R.clone(nestedArray)
console.log(nestedArray[0] === deepCopyWithRamdaClone[0]) // false -- 深拷贝(不同引用)

/**
 * 3. JSON.parse(JSON.stringify(object)) 通常不被推荐,只适合一些特殊情况
 * 对象中不包含Dates, functions, undefined, Infinity, [NaN], 
 * RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types
 */
const referenceObject = 
  string: 'string',
  number: 123, boolean: false,
  null: null,
  notANumber: NaN, // NaN values will be lost (the value will be forced to 'null')
  date: new Date('1999-12-31T23:59:59'),  // Date will get stringified
  undefined: undefined,  // Undefined values will be completely lost, including the key             
  infinity: Infinity,  // Infinity will be lost (the value will be forced to 'null')
  regExp: /.*/, // RegExp will be lost (the value will be forced to an empty object )

const jsonClone = JSON.parse(JSON.stringify(sampleObject))
console.log(jsonClone) //  string: "string", number: 123, boolean: false, null: null, notANumber: null, date: "2000-01-01T04:59:59.000Z", infinity: null, regExp:  

// 4. rfdc(Really Fast Deep Clone) ,非常高效的深度拷贝,速度比lodash的高400%
const clone = require('rfdc')() // Returns the deep copy function
const rfdcClone = clone(array);

 

以上是关于JavaScript中的深拷贝和浅拷贝常用方法总结的主要内容,如果未能解决你的问题,请参考以下文章

深入剖析javaScript中的深拷贝和浅拷贝

JavaScript的深拷贝和浅拷贝

JavaScript中的深拷贝和浅拷贝到底是什么

低门槛彻底理解JavaScript中的深拷贝和浅拷贝

详解Java技术的深拷贝和浅拷贝

JavaScript的深拷贝和浅拷贝