JavaScript有两种数据类型,基础数据类型和引用数据类型。基础数据类型都是按值访问的,我们可以直接操作保存在变量中的实际的值。而引用类型如Array,我们不能直接操作对象的堆内存空间。引用类型的值都是按引用访问的,即保存在变量对象中的是一个地址,该地址与堆内存的实际值相关联。
参考:JavaScript 基础数据类型和引用数据类型
1、深拷贝和浅拷贝的区别
- 浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
- 深拷贝(deep copy):复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。
2、浅拷贝的实现
浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用(地址),所以改变新对象,旧对象也会改变,因为新旧对象共享一块内存。
通过 Object.assign()
拷贝
Object.assign(目标文件, 原文件)
浅拷贝的方法
注意:
- 当对象只有一级属性为深拷贝;
- 当对象中有多级属性时,二级属性后就是浅拷贝;
<script>
// 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(地址)
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
};
var o = {};
// for (var k in obj) {
// // k 是属性名 obj[k] 属性值
// o[k] = obj[k];
// }
// console.log(o);
// o.msg.age = 20;
// console.log(obj);
console.log('--------------');
// Object.assign(目标文件, 原文件) 浅拷贝的方法
Object.assign(o, obj);
console.log(o);
o.msg.age = 20;
console.log(obj);
// 结果就是 o 和 obj 的 msg.age 都变成了20, 因为 o 和 obj 是共享一块内存的
</script>
3、深拷贝的实现
深拷贝的原理:复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。
3.1 递归遍历所有层级,实现深拷贝
我们采用递归
的方式来封装实现深拷贝的函数。
// 1. 使用递归的方式实现深拷贝
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o = {};
// 封装函数
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组(首先判断数组是因为数组也属于对象)
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item);
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj[k], item);
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
};
deepCopy(o, obj);
o.msg.age = 20;
console.log(o);
console.log(obj); // obj.msg.age 还是等于 18 没有改变
var arr = [];
console.log(arr instanceof Object); // true 数组也是对象
3.2 利用 JSON 对象实现深拷贝
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
// 2. 利用 JSON 对象
function deepCopy(obj) {
let _obj = JSON.stringify(obj);
let newObj = JSON.parse(_obj);
return newObj;
}
let newObj = deepCopy(obj);
newObj.msg.age = 22;
console.log(newObj); // newObj.msg.age = 22
console.log(obj); // obj.msg.age 还是等于 18 没有改变
注意: 无法实现对象中方法的深拷贝。
3.3 通过 jQuery 的 extend 方法实现深拷贝
jQuery 的 extend 方法也可以拷贝对象。
$.extend([deep ], target, object1 [, objectN ])
- deep:表示是否深拷贝,true,为深拷贝;false,为浅拷贝。
- target:Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
- object1: objectN 可选。 Object 类型 第一个以及第 N 个被合并的对象。
let a = [0,1,[2,3],4]
let b = $.extend(true, [], a)
a[0] = 1
a[2][0] = 1 // [1,1,[1,3],4]
b // [0,1,[2,3],4]
3.4 Object.assign(),slice,concat 拷贝
当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
(1)通过 Object.assign()
拷贝
Object.assign(目标文件, 原文件)
浅拷贝的方法
var obj = {a:1,b:2}
var newObj = Object.assign({},obj)
(2)使用 concat
实现对数组的深拷贝
concat(arr1, arr2,....)
var arr = [1,2,3]
var newArr = [].concat(arr)
(3)使用 slice
实现对数组的深拷贝
slice(idx1, idx2)
var arr = [1,2,3]
var newArr = arr.slice(0)
// slice 接受两个参数,第一个表示起始索引,第二个表示结束位置索引,省略时表示一直到末尾
参数可以省略
1)没有参数是拷贝数组
2)只有一个参数是从该位置起到结束拷贝数组元素
3)两个参数,拷贝从起始位置到结束位置的元素(不包含结束位置的元素:含头不含尾)
注意:
-
当数组中的元素均为一维是深拷贝。
-
数组中元素一维以上是值的引用。
3.5 lodash函数库实现深拷贝
lodash 很热门的函数库,提供了 lodash.cloneDeep()
实现深拷贝。
_.cloneDeep(value)
value : 要深拷贝的值。
返回拷贝后的值
vue 中使用 :
a. npm i --save lodash 下载依赖
b. import _ from 'lodash' 在 组件 中引入
c. 用法和下面的一样
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
参考:https://www.lodashjs.com/docs/lodash.cloneDeep
3.6 使用扩展运算符实现数组深拷贝
var a=[1,2,3]
var b=[...a];
b.push(4);
console.log(b);//1,2,3,4
console.log(a)//1,2,3