深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
浅拷贝:新对象赋值,只是取的旧对象栈中的值,也就是引用对象的值。浅拷贝出的所有变量都只是指向了一处而已,故会互相干涉。
深拷贝:会在堆里边开辟一个空间,存放自己的对象值。深拷贝出的所有变量都会开辟自己的空间来存放自己的值,因此指向也各不相同,故不会互相干涉。
1.数组的浅拷贝:只克隆第一级,其余的级别还是公用
//@1 slice
let cloneArr = arr.slice(0);
//@2 concat
cloneArr = arr.concat([]);
//@3 ...arr
cloneArr = [...arr];
//@4
cloneArr = [];
arr.forEach((item, index) => {
cloneArr[index] = item;
});
2.对象的浅拷贝
let cloneObj = { ...obj };
cloneObj = Object.assign({}, obj);
3.深拷贝
先把原始对象变为JSON格式字符串,再把字符串变为对象「所涉及的内存,浏览器都会重新开辟一份全新的出来」
3.1 使用递归的方式实现深拷贝
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
2. 通过 JSON 对象实现深拷贝
let arr = [10, 20, 'zhangsan', { name: 'zhangsan' }];
let cloneArr = JSON.parse(JSON.stringify(arr));
console.log(cloneArr === arr); //false
console.log(cloneArr[3] === arr[3]); //false
JSON.stringify的处理缺陷
- 如果值是bigint则会报错 TypeError: Do not know how to serialize a BigInt
- 如果值是 undefined/symbol/function 转换为字符串就没有了
- 如果值是 正则/错误对象实例 直接变为“{}”
- 如果值是 日期对象 变为字符串就回不来的
let obj = {
name: 'zhangsan',
age: 12,
open: true,
n: null,
u: undefined,
sy: Symbol(),
big: 10n,
cours: {
chinese: 100,
english: 50,
math: 150
},
arr: [10, 20, 30],
reg: /\d+/,
err: new Error('错误信息'),
fn: function () { },
time: new Date()
};
let cloneObj = JSON.parse(JSON.stringify(obj));
console.log(cloneObj);//看结果,会有问题
3. 通过jQuery的extend方法实现深拷贝
var array = [1,2,3,4];
var newArray = $.extend(true,[],array);
4.Lodash 或者 underscore 这些类库中都提供了深浅拷贝的办法
yarn add lodash
import _ from 'lodash'
var obj = {id:1,name:{a:'xx'},fn:function(){}};
var obj2 = _.cloneDeep(obj);
obj2.name.a = 'obj2';
console.log(obj,obj2)
5.自己封装深克隆
const isPlainObject = function isPlainObject(obj) {
let proto, Ctor;
if (!obj || Object.prototype.toString.call(obj) !== "[object Object]") return false;
proto = Object.getPrototypeOf(obj);
if (!proto) return true;
Ctor = proto.hasOwnProperty('constructor') && proto.constructor;
return typeof Ctor === "function" && Ctor === Object;
};
const cloneDeep = function cloneDeep(obj, st = new Set()) {
// 为了防止套娃
if (st.has(obj)) return obj;
st.add(obj);
// 原始值类型的克隆:无需克隆,传啥返啥
if (obj == null || !/^(object|function)$/i.test(typeof obj)) return obj;
// 特殊对象值的处理
let isArray = Array.isArray(obj),
isObject = isPlainObject(obj),
type = /^\[object (\w+)\]$/.exec(Object.prototype.toString.call(obj))[1].toLowerCase(),
ctor = obj.constructor;
if (type === "date" || type === "regexp") return new ctor(obj);
if (type === "error") return new ctor(obj.message);
if (type === "function") return function (...params) {
return obj.call(this, ...params);
};
if (!isArray && !isObject) return obj;
// 数组和纯粹对象的处理
let clone = new ctor(),
keys = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj));
keys.forEach(key => {
// 基于递归实现深拷贝
clone[key] = cloneDeep(obj[key], st);
});
return clone;
};
let obj = {
name: 'zhangsan',
age: 12,
cours: {
chinese: 100,
english: 50,
math: 150
},
arr: [10, 20, 30],
reg: /\d+/,
err: new Error('错误信息'),
fn: function () { },
time: new Date()
};
obj.obj = obj; //套娃
let clone = cloneDeep(obj);