本文已参与「新人创作礼」活动,一起开启掘金创作之路。
——Bug修复员-鲁宽宽
前言
简单的事情重复做,温故而知新。
深拷贝与浅拷贝-引用类型时才会考虑
明面上深浅拷贝可以理解为你把磁盘(D:\lu_down/demo.doc)处的word文件复制到了另外一个地址。 其实是指js中对数据的复制操作。 说到js数据,不得不提数据类型,相信你已经想到了:基本数据类型与引用数据类型。 管它什么类型,我们先来一通操作:
var basic = "基本类型";
var obj = {
name: "引用类型-Object",
child: [1, 2, 3] // 留意此处,这里又是一个引用类型
};
console.log(basic); // 基本类型
console.log(obj); // {name: "引用类型-Object", child: Array(3)}
// 现在我们来复制这两个属性,其实就是再定义一个变量的引用
var copyBasic = basic;
var copyObj = obj;
console.log(copyBasic); // 基本类型
console.log(copyObj); // {name: "引用类型-Object", child: Array(3)}
// 至少目前来看一切正常(至少。。。)
// 进行验证,改变copy后的值,在检查原属性值
copyBasic = "copy基本类型";
console.log(basic); // 基本类型
console.log(copyBasic); // copy基本类型
// 快看,完美。并没有影响原属性的值,试试obj吧
copyObj.name = "copy引用类型-Object";
console.log(obj); // {name: "copy引用类型-Object", child: Array(3)}
console.log(copyObj); // {name: "copy引用类型-Object", child: Array(3)}
// 不好,有bug。原属性obj值一起变化了。。。。
当遇到上述问题时,我们就需要考虑是什么问题导致了? 是的,没错,就是数据类型的捣鬼。 那数据类型的不同为什么会导致? 这个就需要扒一扒js的 堆与栈 :
- 栈内存是储存基本数据类型,有固定大小,按值访问;
- 堆内存是存储引用数据类型,无固定大小,引用访问;
描述: copyObj相当于拥有obj堆内存的引子。两者皆可以操作这块内存。 copyBasic相当于在栈空间新开辟了一个空间,把basic的值复制到这个新空间,两者没有直接关系。 上方不伤大雅,下方才是战场~
浅拷贝- 引用类型内皆为基本类型
此情况下可以使用以下方式
es6的解构:适用于对象与数组
assign():只针对于对象
concat()与slice():只针对于数组
一、es6方式
var obj = {
name: "obj",
age: "12"
};
var arr = [1, "2", 3];
// es6方式
var copyObj = {...obj};
copyObj.name = 'copyObj'; // 进行操作
console.log(obj); // {name: "obj", age: "12"}
console.log(copyObj); // {name: "copyObj", age: "12"}
var copyArr = [...arr];
copyArr.push(4); // 进行操作
console.log(arr); // [1, "2", 3]
console.log(copyArr); // [1, "2", 3, 4]
综上:呐喊 First blood,完美首杀。 二、assign()方式
var obj = {
name: "obj",
age: "12"
};
var copyObj = Object.assign({}, obj);
copyObj.name = 'copyObj'; // 进行操作
console.log(obj); // {name: "obj", age: "12"}
console.log(copyObj); // {name: "copyObj", age: "12"}
综上:呐喊 Double kill,双杀到手。 三、concat()与slice() 方式
var arr = [1, "2", 3];
var copyConcatArr = [].concat(arr);
copyConcatArr .push(4); // 进行操作
console.log(arr); // [1, "2", 3]
console.log(copyConcatArr); // [1, "2", 3, 4]
var copySliceArr = Array.prototype.slice.apply(arr);
copySliceArr.push(4); // 进行操作
console.log(arr); // [1, "2", 3]
console.log(copySliceArr); // [1, "2", 3, 4]
综上:呐喊 Triple kill,三杀接上。
深拷贝-引用类型内含有引用类型
什么时候使用深拷贝?
- 数据类型不是基本数据类型
- 拷贝的引用类型内含有引用类型
方案:
JSON.parse():不适用引用类型内的引用类型含有underfined、function(此例子暂未列举)、symbol(此例子暂未列举)场景。
递归函数:一次使用,终身有用。
一、JSON.parse()与JSON.stringifg()
- 当值不含有undefined
var obj = {
name: 'obj',
job: ['object', 'array']
}
var arr = [1, [arrObj: {name: 'arrObj'}]]; // 稍微复杂些验证
var copyObj = JSON.parse(JSON.stringify(obj));
copyObj.job.push('null');
console.log(obj); // {name: "obj", job: Array(2)}
console.log(copyObj); // {name: "obj", job: Array(3)}
var copyArr = JSON.parse(JSON.stringify(arr));
copyArr[1][0].arrObj.name = 'copyArrObj';
console.log(arr[1][0].arrObj); // {name: "arrObj"}
console.log(copyArr[1][0].arrObj); // {name: "copyArrObj"}
综上:呐喊 Quatary kill,四杀到手。
- 当值含有undefined
// undefined
var arr = [1, 2, {a: undefined}];
var copyArr = JSON.parse(JSON.stringify(arr));
console.log(arr); // [1,2,{a:undefined}]
console.log(copyArr); // [1,2,{}]
综上:呐喊 Penta kill,完美五杀。
二、终极必杀-递归函数
function deepCopy(param) {
// 如果param存在且是引用类型,使用拷贝的多为数组或者对象,其他的暂时不考虑
if (!param || !(param instanceof Object)) return param;
// 判断是数组还是对象;
var result = Object.prototype.toString.call(param).indexOf('Array') > -1 ? [] : {};
for (var key in param) {
// 遍历实例上的属性,不包含原型链上的
if(!param.hasOwnProperty(key)) continue;
// 若子属性是引用类型 那么 递归继续拷贝
if(typeof param[key] === 'object' && param[key] !== null) {
resule[key] = deepCopy(param[key]);
}
else {
result[key] = param[key];
}
}
return result;
};
综上:呐喊 Legendary kill,你已超神。