JavaScript 深拷贝、浅拷贝
浅拷贝:浅拷贝只是复制了内存地址,如果原地址中的对象改变了,浅拷贝出来的对象也会相应改变。深拷贝:开辟了一块新的内存存放地址和地址指向的对象,原地址的任何对象改变了,深拷贝出来的对象不变。
浅拷贝数组(只拷贝第一级数组):
1. 直接遍历
var arr = [1,2,3,4];
function copy(arg){
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr.push(arr[i]);
}
return newArr;
}
var newArry = copy(arr);
console.log(newArry);
newArry[0] = 10;
console.log(newArry); // [10,2,3,4]
console.log(arr) // [1,2,3,4]
2.slice
var arr = [1,2,3,4]
var copyArr = arr.slice();
copyArr[0] = 10;
console.log(copyArr); // [10,2,3,4]
console.log(arr); // [1,2,3,4]
slice(start,end),slice() 方法返回一个数组中复制出来的元素组成新数组,start 指起始元素下标,end 指终止元素下标
当 slice() 不带任何参数时,默认返回一个和原数组一样的新数组
3.concat()
var arr = [1,2,3,4]
var copyArr = arr.concat();
copyArr[0] = 10;
console.log(copyArr); // [10,2,3,4]
console.log(arr); // [1,2,3,4]
array.concat(array1,array2,…….,arrayN),concat() 方法用于连接两个或多个数组(不会改变原数组,返回被连接数组的副本)
然而如果第一级数组元素是对象或数组,上面三种方式都失效:
var arr = [
{number:1},
{number:2},
{number:3}
]
var copyArr = arr.slice();
copyArr[0].number = 10;
console.log(copyArr); // [{number: 100}, {number: 2},{number: 3}]
console.log(arr); // [{number: 100}, {number: 2}, {number: 3}]
浅拷贝对象(如果对象中的值不为数组或对象)
1. 直接遍历
var obj = {
name: “ 张三 ”,
job: “ 学生 ”
}
function copy (arg) {
let newobj = {}
for(let item in obj) {
newobj[item] = obj;
}
return newobj;
}
var copyobj = copy(obj)
copyobj.name = “ 李四 ”
console.log(copyobj) // {name: ‘ 李四 ’, job:: ‘ 学生 ’}
console.log(obj) // {name: ‘ 张三 ’, job:: ‘ 学生 ’}
2.ES6 的 Object.assign
var obj = {
name: ‘ 张三 ’,
job: ‘ 学生 ’
}
var copyobj = Object.assign({},obj)
copyobj.name = ‘ 李四 ’
console.log(copyobj) // {name: ‘ 李四 ’, job:: ‘ 学生 ’}
console.log(obj) // {name: ‘ 张三 ’, job:: ‘ 学生 ’}
Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的 target 用法:Object.assign(target, source1, source2); 所以 copyObj = Object.assign({}, obj); 这段代码将会把 obj 中的一级属性都拷贝到 {}中,然后将其返回赋给 copyObj
3.ES6 扩展运算符
var obj = {
name: ‘ 张三 ’,
job: ‘ 学生 ’
}
var copyobj = {…obj}
copyobj.name = ‘ 李四 ’
console.log(copyobj)
console.log(obj)
扩展运算符(…)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中
深拷贝
JSON.stringify() 和 JSON.parse()
用 JSON.stringify 把对象转成字符串,再用 JSON.parse 把字符串转成新的对象。
但是这种方法也有不少坏处,譬如它会抛弃对象的 constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成 Object。
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp 对象是无法通过这种方式深拷贝。
也就是说,只有可以转成 JSON 格式的对象才可以这样用,像 function、undefined、symbol、循环引用的对象没办法转成 JSON。
var obj1 = {fun: function(){console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// ‘function’
console.log(typeof obj2.fun);
// ‘undefined’ <– 没复制
使用递归函数实现一个深拷贝的方法:
function deepClone(obj) {
let 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] = deepClone(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone
}