浅拷贝和浅拷贝的问题,不仅在日常应用中需要注意,而且在面试和笔试中也常被用来考察应聘者,属于“文体两开花”的 points。
什么是深拷贝和浅拷贝呢?
名称
定义
浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递形式的拷贝
深拷贝
对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容
也就是说,对于 a,让 b 对 a 进行拷贝,之后当 a 发生变化,若 b 也跟着发生变化,就是浅拷贝;若 a 发生变化,b 不变化,那就是深拷贝。
基本数据类型
引用数据类型
Number、String、Boolean、Null、Undefined 等
Object、Array、Function 等
基本数据类型是按值访问的,对其的拷贝会直接复制其值保存在新变量中。例如 Number 类型:
var a = 1;
b = a;
console.log(b); // 1
a = 2;
console.log(b); //1(a 的变化不会影响 b 的值)
而“引用数据类型”是按引用访问的,对其直接进行拷贝只会操作引用地址,而不是值本身。例如 Array 类型:
var a = [1, 2, 3];
b = a;
console.log(a); // [1, 2, 3]
console.log(b); // [1, 2, 3]
a[0] = 2;
console.log(a); // [2, 2, 3]
console.log(b); // [2, 2, 3](对数组 a 的改动也影响了数组 b)
其原理如下图所示:
那么,如何对引用数据类型实现深拷贝呢?
显而易见的思路是:既然是因为引用地址造成了无法深拷贝,那就抛开引用地址,直接对值进行遍历,将其拷贝给新的变量。
方法 1: 手工遍历法
let a = [1, [2, 3], 4];
const copy = (obj) => {
let newObj = obj.constructor === Array ? [] : {}
if(typeof obj !== ‘object’) {
return;
}
for(let i in obj) {
newObj[i] = typeof obj[i] === ‘object’ ? copy(obj[i]) : obj[i]
}
return newObj
}
let b = copy(a);
a[1][0] = 3;
console.log(a); // [1, [3, 3], 4]
console.log(b); // [1, [2, 3], 4]
方法 2: JSON 方法
let a = [1, [2, 3], 4];
const copy = (obj) => {
let _obj = JSON.stringify(obj)
let newObj = JSON.parse(_obj)
return newObj
}
let b = copy(a);
a[1][0] = 3;
console.log(a); // [1, [3, 3], 4]
console.log(b); // [1, [2, 3], 4]
方法 3: JQuery-extend 方法
let a = [1, [2, 3], 4];
b = $.extend(true,[],a);
a[1][0] = 3;
console.log(a); // [1, [3, 3], 4]
console.log(b); // [1, [2, 3], 4]