前端浅拷贝与深拷贝的问题
前端的七大数据类型
基础数据类型: String字符串类型
Number 数值类型
Null
undefined
Boolean 布尔类型
Symbol
引用数据类型: Object
原始数据之间的拷贝都是深拷贝
其当原始数据进行赋值的时候,是将自身完全拷贝一份赋值到新的变量上
当新的变量改变后,不会对原始变量产生影响
let a = Symbol('a');
let b = a;
b = Symbol('b')
console.log('a==>', a);//a==> Symbol(a)
console.log('b==>', b);//b==> Symbol(b)
let c = 10;
let d = c;
c = 20
console.log('c==>', c);//c==> 20
console.log('d==>', d);//d==> 10
let e = 'Hello world!';
let f = e;
f = 'I am a good man';
console.log("e ==>", e);//e ==> Hello world!
console.log("f ==>", f);//f ==> I am a good man
引用类型数据的拷贝就不一样了,
因为 引用类型数据直接赋值给变量的话,它只是把一个引用地址复制过去了,
这样当变量发生改变时,复制的变量和原始变量会同时改变
let arr = [5, 8];
let arr1 = arr;
arr1[0] = 3;
// arr[0] = 3;
/*
这里不管是修改原copy变量,还是copy后的变量,结果都是一样,两个变量都指向同一个堆内存,修改一方必定影响另一方
*/
console.log('arr ==>', arr); // [3,8]
console.log('arr1 ==>', arr1);// [3,8]
let obj = { name: '小红' };
let obj1 = obj;
obj1.age = 18;
console.log('obj ==>', obj);//obj ==> {name: "小红", age: 18}
console.log('obj1 ==>', obj1);//obj1 ==> {name: "小红", age: 18}
上面的就称为浅拷贝,它只是单纯的拷贝一个引用地址,
深拷贝需要分离两个引用,达到互不影响的效果
数组的分离
//方法1
let array = [1, 2, 3, 4, 5];
let array1 = array.concat();
array1[0] = 9;
console.log('array==>', array) //[1, 2, 3, 4, 5]
console.log('array1==>', array1) // [9, 2, 3, 4, 5]
/*
将concat改成slice也可以有相同效果
es6 ==> array1 = [...array] 一样效果
原理是利用这两个数组方法会返回一个新的数组,达到分离的效果
缺点:这两种方法都只适用于一层嵌套,在数组嵌套数组后不能达到效果[1, 2, 3, 4, [5,6]],改变5或者6,都会改变
*/
//方法2
let array = [1, 2, 3, 4, [5, 6]];
let array1 = JSON.parse(JSON.stringify(array));
array1[4][0] = 9;
console.log('array==>', array)
console.log('array1==>', array1)
//利用JSON.stringify将数组变成JSON字符串,再转回来,能解决多重嵌套问题
对象的分离
let object = { person: '小孩', age: 18 };
let object1 = Object.assign({}, object);
object1.book = "javascript";
console.log("object==>", object)
console.log("object1==>", object1)
/*与上面数组方法一样
es6 ==> object1 = {...object}
Object.assign也是返回一个新的合并对象,但是也是只能一层,对于多层嵌套的不行
*/
//方法2
let object = { person: '小孩', age: 18, book: ['java', 'c++'] };
let object1 = JSON.parse(JSON.stringify(object));
object1.book[0] = "javascript";
console.log("object==>", object)
console.log("object1==>", object1)
//利用JSON.stringify将数组变成JSON字符串,再转回来,能解决多重嵌套问题
使用递归遍历,进行深复制
JSON.parse(JSON.stringify(object));
按道理上面这个方法能解决一大部分的深拷贝问题,
但是对象中含有函数时,这个方法就无能为力了
let object = {
person: '小孩', age: 18, book: ['java', 'c++'],
fn: function () {
console.log('我是一个函数');
}
};
console.log(JSON.stringify(object)) //{"person":"小孩","age":18,"book":["java","c++"]}
/*
很明显,当对象中含有函数时候,JSON.stringify会直接把函数去掉
*/
//前置知识
// 判断一个元素是什么类型
console.log(Object.prototype.toString.call('ABC'));//[object String]
console.log(Object.prototype.toString.call(10));//[object Number]
console.log(Object.prototype.toString.call([1, 3, 8]));//[object Array]
console.log(Object.prototype.toString.call({ name: '小红' }));//[object Object]
// 封装一个判断元素类型的函数
function typeOfEle(ele) {
return Object.prototype.toString.call(ele).slice(8, -1);
}
// 迭代深拷贝函数
function deepCopy(origin) {
// 这是一个拦截层,当数据迭代到不是对象或数组时返回
if ((typeOfEle(origin) != "Object") && (typeOfEle(origin) != "Array")) {
return origin;
}
let target = null; //copy复制的数据类型不定
if ((typeOfEle(origin) == "Object")) {
// 如果拷贝的数据类型是对象
target = {};
} else {
// 不然拷贝的数据类型就是数组了
target = [];
}
// 遍历原始的数据
for (let key in origin) {
// 当遍历的数据不是对象或者数组时候,进行拷贝
if (typeOfEle(origin[key]) != "Object" && (typeOfEle(origin[key]) != "Array")) {
target[key] = origin[key];
// 当遍历的数据是对象或者数组的时候,进行迭代再拷贝
} else {
target[key] = deepCopy(origin[key]);
}
}
return target;
}
//==========================demo================
let object = {
person: '小孩', age: 18, book: ['java', 'c++'], fn: function () {
console.log('我是一个函数');
},
demo: {
f1() {
return "我是f1";
},
f2() {
return "我是f2";
}
}
};
let object1 = deepCopy(object);
object1.book[0] = 'javascript';
object1.demo.f1 = function () {
return "我是新的f1";
}
console.log("object==>", object)
console.log("object1==>", object1)
console.log("object.demo.f1==>", object.demo.f1())
console.log("object1.demo.f1==>", object1.demo.f1())
demo结果图
总结
使用迭代遍历进行深拷贝基本能解决深拷贝问题,但是迭代本身具有性能问题
请根据具体需要使用(本人能力有限,这里挖个坑,以后有好的解决方案继续更新)