浅拷贝与深拷贝
1. js的数据类型有两种:
基本数据类型:number、string、boolean、undefined、null、symbol、bigInt;
引用数据类型:Object。
2. 数据类型的存储方式:
基本数据类型:基本类型的值存储在栈内存中,栈内存有固定大小;
引用数据类型:引用类型的值是对象,该对象保存在堆内存中,而堆内存的指针保存在栈内存中。
3. 拷贝效果:
基本数据类型:拷贝得到副本的值与原变量的值一样,操作其中一个对象不会对另一个对象产生影响;
引用数据类型:
浅拷贝:拷贝得到副本的值是原变量在栈内存的地址,副本与原变量同时指向同一个地址,操作任意一个对象都会对另一个对象产生影响;
深拷贝:拷贝得到的副本在堆中重新分配内存,与原变量的值相同,但是栈中的地址不同,两个变量间互不影响。
4. 浅拷贝
Array.prototype.slice()
//例1:
let a=[1,2,3];
let b=a.slice();
console.log(b);//[1,2,3]
a.push(4)
console.log(b);//[1,2,3],b的值没有随着a的改变而变化
//例2:
let a=[1,2,3,[4,5]];
let b=a.slice();
console.log(b);//[1,2,3,[4,5]]
a[3].push(6);
console.log(b); //[1,2,3,[4,5,6]],b的值随着a的改变而变化了
Array.prototype.concat()
//例1:
let a = [1, 2, 3];
let b = a.concat();
console.log(b); //[1,2,3]
a.push(4);
console.log(b); //[1,2,3],b的值没有随着a的改变而变化
//例1:
let a = [1, 2, 3,[4,5]];
let b = a.concat();
console.log(b); //[1, 2, 3,[4,5]]
a[3].push(6);
console.log(b); //[1, 2, 3,[4,5,6]],b的值随着a的改变而变化了
Array.from()
//例1:
let a = [1, 2, 3];
let b = Array.from(a);
console.log(b); //[1,2,3]
a.push(4);
console.log(b);[1,2,3],b的值没有随着a的改变而变化
//例2:
let a = [1, 2, 3,[4,5]];
let b = Array.from(a);
console.log(b); //[1, 2, 3,[4,5]]
a[3].push(6);
console.log(b); //[1, 2, 3,[4,5,6]],b的值随着a的改变而变化了
Object.assign()
// Object.assign()中的花括号叫目标对象,后面的参数是源对象。对象属性合并是指:将源对象里面的属性添加到目标对象中去,若两者的属性名有冲突,后面的将会覆盖前面的。
//例1:
let a = { name: 'yingzi' }
let b = Object.assign({}, a);
console.log(b); //{name: 'yingzi'}
a.age = 18;
console.log(b); //{name: 'yingzi'},b的值没有随着a的改变而变化
//例2:
let a = { name: 'yingzi', nationality: {country:'中国',city:'广州'}}
let b = Object.assign({}, a);
console.log(b); //{ name: 'yingzi', nationality: {country:'中国',city:'广州'}}
a.nationality.city = "上海"
console.log(b); //{ name: 'yingzi', nationality: {country:'中国',city:'上海'}},b的值随着a的改变而变化了
扩展运算符
//例1:
let a = [1, 2, 3];
let b = [...a];
console.log(b); //[1, 2, 3]
a.push(4);
console.log(b); //[1, 2, 3],b的值没有随着a的改变而变化
//例2:
let a = [1, 2, 3,[4,5]];
let b = [...a];
console.log(b); //[1, 2, 3,[4,5]]
a[3].push(6);
console.log(b); //[1, 2, 3,[4,5,6]],b的值随着a的改变而变化了
5. 深拷贝
JSON.stringify()、JSON.parse()
//例1:
let a = { name: 'yingzi' }
let b = JSON.parse(JSON.stringify(a));
console.log(b); //{name: 'yingzi'}
a.age = 18;
console.log(b); //{name: 'yingzi'},b的值没有随着a的改变而变化
//例2:
let a = { name: 'yingzi', nationality: {country:'中国',city:'广州'}}
let b = JSON.parse(JSON.stringify(a));
console.log(b); //{ name: 'yingzi', nationality: {country:'中国',city:'广州'}}
a.nationality.city = "上海"
console.log(a); ////{ name: 'yingzi', nationality: {country:'中国',city:'上海'}}
console.log(b); //{ name: 'yingzi', nationality: {country:'中国',city:'广州'}},b的值没有随着a的改变而变化
JSON方法的缺点:不可以拷贝undefined、function、RegExp等类型。
递归遍历
// 递归函数:
function deepCloneRecur(obj) {
// 判断拷贝对象的类型,从而给新对象赋相应的类型
var newObj = Array.isArray(obj) ? [] : {};
// 判断当前拷贝的对象不为空,且类型是object才进行递归进行深度遍历
if (obj && typeof obj === 'object') {
for (var key in obj) {
if (obj.hasOwnProperty(key)) { //自身的属性才递归赋值
if (obj[key] && typeof obj[key] === 'object') {
newObj[key] = deepCloneRecur(obj[key]);
} else {
newObj[key] = obj[key];
}
}
}
}
return newObj
}
//例1:
let a = { name: 'yingzi', nationality: { country: '中国', city: '广州' } };
let b = deepCloneRecur(a);
console.log(b); //{ name: 'yingzi', nationality: { country: '中国', city: '广州' } }
//例2:
a.nationality.city = "上海";
console.log(a); //{ name: 'yingzi', nationality: { country: '中国', city: '上海' } }
console.log(b); //{ name: 'yingzi', nationality: { country: '中国', city: '广州' } },b的值没有随着a的改变而变化
因为for in 循环会枚举所有可枚举属性,包含原型上的,所以递归函数中要判断当前遍历的属性是否属于自己身上的属性。