写在前面
数组的深浅克隆在前端开发中经常使用,面试也经常被问到,本篇文章将详细介绍深浅克隆。
深浅克隆
深浅克隆的定义和区别
1、浅克隆: 只拷贝一层对象的属性,当一个属性的值为基本类型值时,就会正常拷贝,且改变新对象,不会改变原对象中的这个值;当一个属性的值为引用类型值时,拷贝的就是地址,当改变这其中值的时候,也同时会改变原对象中的值。
2、深克隆: 深拷贝则不同,它是将对象中的每一层都拷贝到新对象上,每个属性都指向了不同的地址,这样,无论怎么修改新对象,都不会改变原对象的值
实现深浅克隆
实现浅克隆
展开运算符(…)
var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];
var brr = [...arr];
console.log(arr, brr); // [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2] [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2]
console.log(arr === brr); // false
// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = '改变'
console.log(arr, brr); // [{...}, 1, 2] [{...}, '改变', 2]
// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";
console.log(arr, brr); // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
Object.assign()
var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];
var brr = Object.assign([], arr)
// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = '改变'
console.log(arr, brr); // [{...}, 1, 2] [{...}, '改变', 2]
// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";
console.log(arr, brr); // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
Array.slice()
var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];
var brr = arr.slice(0)
console.log(arr, brr); // [{...}, 1, 2] [{...}, 1, 2]
// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = 5;
console.log(arr, brr); // [{...}, 1, 2] [{...}, 5, 2]
// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";
console.log(arr, brr); // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
Array.from()
var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];
var brr = Array.from(arr);
console.log(arr, brr); // [{...}, 1, 2] [{...}, 1, 2]
// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = 5;
console.log(arr, brr); // [{...}, 1, 2] [{...}, 5, 2]
// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";
console.log(arr, brr); // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
实现深克隆
JSON.stringify()和JSON.parse()组合
var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];
var brr = JSON.parse(JSON.stringify(arr));
console.log(arr, brr); // [{...}, 1, 2] [{...}, 1, 2]
brr[0].name = '对象';
console.log(arr, brr); // [{name: '数组', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
上面我们实现了深克隆,但是这种方法有一个弊端,它在克隆过程中会忽略正则、日期、Symbol、undefined和循环引用对象等
var json = {name: '数组', reg: /^[0-9]$/}
console.log(JSON.parse(JSON.stringify(json))); // { name: "数组", reg: {} }
看到结果,你会发现,正则表达式在深克隆之后变成了空对象,所以这个方法是有一定的弊端的,下面,我们自己来写一个深克隆方法~
自己实现
1、第一种方法
// 判断类型函数
function _type(data) {
return Object.prototype.toString.call(data);
}
// 深克隆
function _deepClone(obj) {
// 判断传入数据类型
if(obj === null) return null;
if(typeof obj !== "object") return obj;
if(_type(obj) === "[object RegExp]") return new RegExp(obj);
if(_type(obj) === "[object Date]") return new Date(obj);
// 创建对象
let newObj = new obj.constructor;
for(let key in obj) {
if(!obj.hasOwnProperty(key)) break;
// 只要是私有属性就递归调用
newObj[key] = _deepClone(obj[key])
}
return newObj
}
// 测试
var json = {name: '数组', reg: /^[0-9]$/}
var newJson = _deepClone(json);
console.log(json, newJson) // { name: "数组", reg: /^[0-9]$/ } { name: "数组", reg: /^[0-9]$/ }
newJson.name = '对象';
console.log(json, newJson); // { name: "数组", reg: /^[0-9]$/ } { name: "对象", reg: /^[0-9]$/ }
2、第二种方法
function _type(data) {
return Object.prototype.toString.call(data)
}
function _deepClone(obj) {
let newObj = new obj.constructor;
for(let key in obj) {
let item = obj[key],
itemType = _type(obj[key]);
if(!obj.hasOwnProperty(key)) break;
// 只有当前属性对应值不是nullq且为“object”时,才递归
if(obj !== null && typeof item === "object") {
// 如果值为正则,日期类型,需要特殊处理
if(/(RegExp|Date)/.test(itemType)) {
newObj[key] = new obj.constructor(item);
continue;
}
newObj[key] = _deepClone(item);
continue;
}
newObj[key] = item;
}
return newObj;
}
// 测试
var json = {name: '数组', reg: /^[0-9]$/, u: undefined, s: Symbol('111')}
var newJson = _deepClone(json);
console.log(json, newJson) // { name: "数组", reg: /^[0-9]$/, u: undefined, s: Symbol(111) } { name: "数组", reg: /^[0-9]$/, u: undefined, s: Symbol(111) }
newJson.name = '对象';
console.log(json, newJson); // { name: "数组", reg: /^[0-9]$/, u: undefined, s: Symbol(111) } { name: "对象", reg: /^[0-9]$/, u: undefined, s: Symbol(111) }
面试官问:你了解深浅克隆吗?
相信大家在面试中都被问过这个问题,那么你答得怎么样?我们可以从以下几个角度按照顺序回答这个问题:
深浅克隆的定义
深浅克隆的区别
深浅克隆如何实现
深浅克隆在工作中的应用
如果大家有更好的想法回答这个问题,欢迎在评论区交流~
写在最后
最后,欢迎大家关注我的公众号「web前端日记」,关注有福利领取
~