「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
复制对象或数组的常用方法是只进行浅拷贝,所以深度嵌套的引用是一个问题。如果 JavaScript 对象包含其他对象,就需要深拷贝。
什么是浅拷贝
对象或数组的浅拷贝就是创建对对象内部原始值的新引用,并且复制它们。所以对原始数组的更改不影响复制的数组。
如果仅复制了对数组的引用,就会改变原有的数组。
示例:
//仅复制引用
let array = [1,2,3,4,5];
let arrayCopy = array;
arrayCopy[0] = 22;
console.log(array); //[22,2,3,4,5]
console.log(arrayCopy); //[22,2,3,4,5]
console.log(array==arrayCopy); //true
// 扩展运算符浅拷贝
let array1 = [1,2,3,4,5];
let arrayCopy1 = [...array1];
arrayCopy1[0] = 11;
console.log(array1); //[1,2,3,4,5]
console.log(arrayCopy1); //[11,2,3,4,5]
console.log(arrayCopy1); //false
四种浅拷贝方法
扩展运算符(...)
适用于创建数组或对象的浅拷贝,没有嵌套的情况。
上面的示例中使用就是拓展运算符。
.slice()
对于数组,使用内置.slice()方法的方法与扩展运算符相同
示例:
let array2 = [1,2,3,4,5];
let arrayCopy2 = array2.slice();
arrayCopy2[0] = 22;
console.log(array2); //[1,2,3,4,5]
console.log(arrayCopy2); //[22,2,3,4,5]
console.log(array2==arrayCopy2); //false
.assign()
使用Object.assign()可以创建相同类型的浅拷贝,可以与任何对象或数组使用。
示例:
let array3 = [1,2,3,4,5];
let arrayCopy3 = [];
Object.assign(arrayCopy3, array3);
arrayCopy3[0] = 33;
console.log(array3); //[1,2,3,4,5]
console.log(arrayCopy3); //[33,2,3,4,5]
console.log(array3==arrayCopy3); //false
Array.from()
使用 Array.from()也可以复制数组。
let array4 = [1,2,3,4,5];
let arrayCopy4 = Array.from(array4);
arrayCopy4[0] = 44;
console.log(array4); //[1,2,3,4,5]
console.log(arrayCopy4); //[44,2,3,4,5]
console.log(array4==arrayCopy4); //false
浅拷贝是指仅复制一个级别的事实,这对于仅包含原始值的数组或对象可以正常工作。
对于包含其他对象或数组的对象和数组,复制这些对象需要深拷贝。否则,对嵌套引用所做的更改将更改嵌套在原始对象或数组中的数据。
如果一个对象或数组包含其他对象或数组,浅拷贝会出错,因为嵌套对象实际上并未被克隆。
对于深度嵌套的对象,需要使用深拷贝。
什么是深拷贝
当包含数组的JS对象嵌套的比较深时,浅拷贝的复制只具有引用的第一层,更深的值仍链接在一起。
示例:
let array5 = [{id:1},{id:2},{id:3},{id:4},{id:5}];
let arrayCopy5 = [...array5];
arrayCopy5[0].id = 55;
console.log(array5[0].id); //55
console.log(arrayCopy5[0].id); //55
console.log(array5==arrayCopy5); //false
要解决这个问题,需要使用深拷贝。深拷贝适用于包含其他对象或数组的对象和数组,复制这些对象需要深拷贝。深拷贝的常用方法有以下几种。
五种深拷贝方法
lodash库
lodash库是 JavaScript 最常见方式深拷贝方式。Lodash 的名称来自被引用为下划线 (_)、“低破折号”或简称 lodash 的库。
示例:
import _ from "lodash" // 引入lodash库
const array6 = [{id:1},{id:2},{id:3},{id:4},{id:5}];
// 浅拷贝
const arrayCopy6 = _.clone(array6)
console.log(array6[0] === arrayCopy6[0]) // true
// 深拷贝
const arrayCopy7 = _.cloneDeep(array6)
console.log(array6[0] === arrayCopy7[0]) // false
Ramda
函数式编程库Ramda包含R.clone(),可以对对象或数组进行深度复制。R.clone()from Ramda 等价于_.cloneDeep()for lodash,因为 Ramda 没有浅拷贝辅助方法。
import R from "ramda" // 引入ramda库
const array7 = [{id:1},{id:2},{id:3},{id:4},{id:5}];
// 浅拷贝
const arrayCopy8 = [...array7]
console.log(array7[0] === arrayCopy8[0]) // true
// 深拷贝
const arrayCopy9 = R.clone(nestedArray)
console.log(array7[0] === arrayCopy9[0]) // false
自定义函数
使用递归编写一个深拷贝函数也很简单。
const deepCopyFunction = (inObject) => {
let outObject, value, key
if (typeof inObject !== "object" || inObject === null) {
return inObject;
}
// 创建一个数组或对象来保存值
outObject = Array.isArray(inObject) ? [] : {}
for (key in inObject) {
value = inObject[key]
// 嵌套对象(包括数组)的递归(深度)复制
outObject[key] = deepCopyFunction(value)
}
return outObject
}
let array7 = [{id:1},{id:2},{id:3},{id:4},{id:5}];
// 浅拷贝
let arrayCopy9 = [...array7];
console.log(array7[0] === arrayCopy8[0]) // true
// 深拷贝
let arrayCopy10 = deepCopyFunction(array7);
console.log(array7[0] === arrayCopy10[0]) // false
JSON.parse/stringify
JSON.parse():从一个字符串中解析出json对象
JSON.stringify():从一个对象中解析出字符串
用法非常简单:
let arrayCopy9 = JSON.parse(JSON.stringify(array7));
号称真正的快速深拷贝 rdfc
github地址:# rfdc
据说,该库比lodash快400%进行深度复制。并且rfdc库支持所有类型。
使用示例:
const clone = require('rfdc')()
clone({a: 1, b: {c: 2}}) // => {a: 1, b: {c: 2}}