1、概要
- 本文主要讲述数据类型。
js
中深拷贝与浅拷贝的概念。- 介绍深拷贝与浅拷贝的实现方法。
2、数据类型
- 基本数据类型
- 基本的数据类型有:
undefined
,boolean
,number
,string
,null
,symbol
。 - 基本类型的访问是按值访问的,就是说你可以操作保存在变量中的实际的值。
- 任何方法都无法改变一个基本类型的值。
var str = 'abc123';
str.replace('abc', 123); // '123123'
console.log(str); // 'abc123'
- 基本类型的比较是值的比较。
- 基本类型的值是存放在栈内存中。
- 引用数据类型
- 除基本类型之外就是引用数据类型,也可以说就是对象了(
Object
,Array
,Function
,Math
,Data
,RegExp
等)。 - 引用类型的值是可变的。
- 引用类型的值是同时保存在栈内存和堆内存中的对象。
- 引用类型的比较是引用的比较
3、基本概念
- 简单来说,如果对象B拷贝A,当修改A时,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝。
- 所有
基本数据类型
的拷贝都是深拷贝。
// 对基本数据类型的直接赋值是深拷贝
var a = 1;
b = a;
a = 2;
console.log(a); // 2
console.log(b); // 1
// 对引用数据类型的直接赋值是浅拷贝(拷贝的是引用的地址)
var obj1 = { name: 'ZhangSan', age: 21 };
var obj2 = obj1;
obj1.age = 32;
console.log(obj1.age); // 32
console.log(obj2.age); // 32
- 后续本文讲述的深拷贝和浅拷贝都是针对
引用数据类型
来阐述的
4、如何实现深拷贝
1、数组的拷贝
// 浅拷贝,直接赋值
var a = [1,2,3,4,5];
var b = a;
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [2, 2, 3, 4, 5]
// cancat,简单数组的深拷贝
var a = [1,2,3,4,5];
var b = [].concat(a);
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [1, 2, 3, 4, 5]
// slice,简单数组的深拷贝
var a = [1,2,3,4,5];
var b = a.slice();
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [1, 2, 3, 4, 5]
// ...运算符,简单数组的深拷贝
var a = [1,2,3,4,5];
var b = [...a];
a[0] = 2;
console.log(a); // [2, 2, 3, 4, 5]
console.log(b); // [1, 2, 3, 4, 5]
cancat
,slice
,...运算符
的不足
var a = [1,[1,2],3,4,5];
var b = a.slice();
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [2,2], 3, 4, 5]
var a = [1,[1,2],3,4,5];
var b = [].concat(a);
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [2,2], 3, 4, 5]
var a = [1,[1,2],3,4,5];
var b = [...a];
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [2,2], 3, 4, 5]
从上面的结果可以看到,cancat
,slice
,...运算符
只能对数组的一级元素为基本类型的数据实现深拷贝,对于引用类型的元素还是浅拷贝。
2、对象的拷贝
...运算符
var obj1 = {
name: 'ZhangSan',
age: 24,
team: {
name: 'obj1',
number: 10
}
};
var obj2 = {...obj1};
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
console.log(obj1); // {name: "LiSi", age: 42, team: {name: "test", number: 20}}
console.log(obj2); // {name: "ZhangSan", age: 24, team: {name: "test", number: 20}}
Object.assign()
方法
var obj1 = {
name: 'ZhangSan',
age: 24,
team: {
name: 'obj1',
number: 10
}
};
var obj2 = Object.assign({}, obj1);
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
console.log(obj1); // {name: "LiSi", age: 42, team: {name: "test", number: 20}}
console.log(obj2); // {name: "ZhangSan", age: 24, team: {name: "test", number: 20}}
从上面可以看到...运算符
和 Object.assign()
方法也只能实现对一级数据为基本数据类型的拷贝,对于一级数据类型为引用类型的数据无法实现深拷贝。
3、 使用JSON.parse
和JSON.stringify
var a = [1,[1,2],3,4,5];
var b = JSON.parse(JSON.stringify(a));
a[0] = 2;
a[1][0] = 2;
console.log(a); // [2, [2,2], 3, 4, 5]
console.log(b); // [1, [1,2], 3, 4, 5]
var obj1 = {
name: 'ZhangSan',
age: 24,
team: {
name: 'obj1',
number: 10
}
};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
console.log(obj1); // {name: "LiSi", age: 42, team: {name: "test", number: 20}}
console.log(obj2); // {name: "ZhangSan", age: 24, team: {name: "obj1", number: 10}}
ps: 并不是所有的数据类型都可以被JSON.stringify
。
4、递归
// 此方法仅仅提供思路,并非最佳实践
function deepClone(target, type) { // target:目标对象,type:true/false =>深拷贝/浅拷贝,默认false
if(!type) return target
const baseType = ['string','number','boolean','undefined','null','symbol']
if(!target || baseType.includes(typeof target)) return target
let cloneData = Array.isArray(target) ? [] : {};
if(typeof target === 'object') {
for(key in target) {
if(target.hasOwnProperty(key)) {
if(target[key] && typeof target[key] ==="object") {
cloneData[key] = deepClone(target[key], true);
} else {
cloneData[key] = target[key];
}
}
}
}
return cloneData
}
var obj1 = {
name: 'ZhangSan',
age: 24,
team: {
name: 'obj1',
number: 10,
person: {
name: '二狗子',
sex: '女'
}
}
};
var obj2 = deepClone(obj1, true);
obj1.name = 'LiSi';
obj1.age = 42;
obj1.team.name = 'test';
obj1.team.number = 20;
obj1.team.person = {
name: '刘二哈',
sex: '男'
}
console.log(obj1); //
console.log(obj2); //
5.总结
- 主要讲述了基本数据类型和引用数据类型的概念。
- 深拷贝和浅拷贝的概念和区别。
- 介绍了几种实现深拷贝的方法。
- 递归的方法需要自己根据需求选择去完善。