JavaScript 深浅拷贝以及特殊类型拷贝问题
1. 数组
-
当数组只有一个层级时
- rest运算符,如下代码
const arr = [1, 2, 3, 4, 5]; const arr2 = [...arr]; console.log(arr === arr2); //false arr[0] = 111; console.log(arr[0], arr); // 111 [111, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
- slice方法,如下代码
const arr = [1, 2, 3, 4, 5]; const arr2 = arr.slice(); console.log(arr === arr2); //false arr[0] = 222; console.log(arr[0], arr); // 222 [222, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
- concat方法,如下代码
const arr = [1, 2, 3, 4, 5]; const arr2 = arr.concat(); console.log(arr === arr2); //false arr[0] = 333; console.log(arr[0], arr); // 333 [333, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
- for循环,如下代码
const arr = [1, 2, 3, 4, 5]; let arr2 = []; for (let i = 0; i < arr.length; i++) { arr2[i] = arr[i]; } console.log(arr2 === arr); // false arr[0] = 123; console.log(arr[0], arr); //123 [123, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
- for of循环,如下代码
const arr = [1, 2, 3, 4, 5]; let arr2 = []; for (let item of arr) { arr2.push(item); } console.log(arr2 === arr); // false arr[0] = 123; console.log(arr[0], arr); //123 [123, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
- for in循环,如下代码
const arr = [1, 2, 3, 4, 5]; let arr2 = []; for (let idx in arr) { arr2.push(arr[idx]); } console.log(arr2 === arr); // false arr[0] = 123; console.log(arr[0], arr); //123 [123, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
- JSON.parse以及JSON.stringify,如下代码
const arr = [1, 2, 3, 4, 5]; let arr2 = JSON.parse(JSON.stringify(arr)); console.log(arr2 === arr); // false arr[0] = 123; console.log(arr[0], arr); //123 [123, 2, 3, 4, 5] console.log(arr2[0], arr2); // 1 [1, 2, 3, 4, 5]
总结:当数组中的item为复杂类型时,以上方法为浅拷贝
2. 对象
-
当对象里面的每项都为简单类型时
- Object.assign,当对象里面每项都为简单类型时,是深拷贝,当存在复杂类型时,就是浅拷贝。
const obj = { a: 1, b: true, c: null, d: NaN, e: undefined, f: function () {}, g: 'string', }; const obj2 = Object.assign({}, obj); console.log(obj === obj2); // false obj.a = 2; console.log(obj); // {a: 2, b: true, c: null, d: NaN, e: undefined, …} console.log(obj2); // {a: 1, b: true, c: null, d: NaN, e: undefined, …}
总结:同样对于for in方法遍历,rest运算符,JSON.parse以及JSON.stringify,需要判断对象中的数据类型,看是深拷贝,还是浅拷贝
3. 特殊类型的拷贝问题
-
使用
JSON.parse(JSON.stringify())
拷贝数组const arr = [undefined, function () {}, () => {}, NaN]; let arr2 = JSON.parse(JSON.stringify(arr)); console.log(arr); // [undefined, ƒ, ƒ, NaN] console.log(arr2); // [null, null, null, null]
当数据类型为
undefined
,function
,arrow function
,NaN
时,使用JSON.parse(JSON.stringify())
后,数据会转换成null
-
使用
JSON.parse(JSON.stringify())
拷贝对象const obj = { a: undefined, b: function () {}, c: () => {}, d: NaN, }; const obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj); // {a: undefined, d: NaN, b: ƒ, c: ƒ} console.log(obj2); // {d: null}
当对象类型中存在
undefined
,function
,arrow function
,NaN
时,使用JSON.parse(JSON.stringify())
后,undefined
,function
,arrow function
,会直接丢失,NaN
会被转换为null
-
当存在类型为
Date
,Error
,RegExp
,时const obj = { a: new Date(), b: new Error(), c: /\d+/, }; const obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj); // {a: undefined, d: NaN, b: ƒ, c: ƒ} console.log(obj2); // {d: null}
当对象类型中存在Date
,Error
,RegExp
时,使用JSON.parse(JSON.stringify())
后,Error
,RegExp
会被转换为{}
,Date
格式会被转换
4. 深拷贝,考虑所有可能的类型
- typescript
const obj = {
a: 1,
b: '2',
c: null,
d: undefined,
e: NaN,
f: function () {},
g: () => {},
h: { h1: '' },
i: [1, 2, 3, 4],
j: /\d+/gi,
k: new Error('error'),
l: new Map(),
m: new Set(),
n: new Date(),
o: true,
};
function deepCopy<T extends Array<T> | { [key: string]: any }>(data: T): T {
if (Array.isArray(data)) {
return data.map((item) => deepCopy(item)) as T;
}
const result: T = {} as T;
const getType = (_v: any) => Object.prototype.toString.call(_v);
if (getType(data) === '[object Object]') {
return Object.keys(data).reduce((result, key) => {
const _type = getType(data[key]);
switch (_type) {
case '[object RegExp]':
case '[object Date]':
case '[object Error]':
result[key] = data[key].constructor(data[key]);
break;
case '[object Object]':
case '[object Array]':
result[key] = deepCopy(data[key]);
break;
default:
result[key] = data[key];
}
return result;
}, result);
}
return data;
}
- javascript
function deepCopy(data) {
if (Array.isArray(data)) {
return data.map((item) => deepCopy(item));
}
const result = {};
const getType = (_v) => Object.prototype.toString.call(_v);
if (getType(data) === '[object Object]') {
return Object.keys(data).reduce((result, key) => {
const _type = getType(data[key]);
switch (_type) {
case '[object RegExp]':
case '[object Date]':
case '[object Error]':
result[key] = data[key].constructor(data[key]);
break;
case '[object Object]':
case '[object Array]':
result[key] = deepCopy(data[key]);
break;
default:
result[key] = data[key];
}
return result;
}, result);
}
return data;
}