浅层克隆
要将 对象 obj 克隆给对象 obj1
//浅层克隆实现
var obj = {
name: 'abc',
age: 20,
sex: 'male',
arr: ['aaa', 'bbb', 'ccc']
}
function copy(origin, target) {
var target = target || {};
//为了防止用户不传 target(容错),给了参数就直接用,不给就当空对象
for (var prop in origin) {
target[prop] = origin[prop];
}
return target;
}
var obj1 = copy(obj);
然而,浅层克隆存在相应的问题
浅层克隆为什么会出现这种问题
首先我们需要清楚 JavaScript 中的数据类型总体来说分为两种,他们分别是:基本数据类型 和 复杂数据类型
基本数据类型(值类型):数值型(Number),字符类型(String),布尔值型(Boolean),null 和 underfined
复杂数据类型(引用数据类型):函数,对象,数组等
- 基本数据类型:这些值都有“固定的大小”,往往都保存在"栈内存"中(闭包除外)。变量之间的互相赋值,是指开辟一块新的内存空间,将变量值赋给新变量保存到新开辟的内存里面;之后两个变量的值变动互不影响
- 复杂数据类型:引用数据类型的值是保存在堆内存中的对象。变量之间的互相赋值,只是指针的交换,而并非将对象(普通对象,函数对象,数组对象)复制一份给新的变量,对象依然还是只有一个,只是多了一个指引
因此在浅层克隆中
- 基本数据为值传递,复杂数据类型(对象,数组)仍为引用传递。引用传递使对象指向相同的地址空间,改变一个对象,另一个也会随之改变。
而我们需要的克隆 是所有元素或属性均完全复制,与原对象完全脱离,也就是说所有对于新对象的修改都不会反映到原对象中。
深度克隆思路
需要一个分析环节,分析待拷贝的是什么,是原始值就按原来的方法拷贝过来,是引用值就分析是数组还是对象。如果是数组,就新建一个数组;如果是对象,就新建一个对象。再一层层看,形成一个递归。
思路步骤
- 先把所有的值都遍历一遍(看是引用值和原始值)
- 判断是原始值,还是引用值?用 typeof 判断是不是 object。
- 如果是原始值就直接拷贝。
- 如果是引用值,判断是数组还是对象。
- 判断是数组还是对象
- 如果是数组,就新建一个空数组。
- 如果是对象,就新建一个空对象。
- 建立了数组以后,如果是挨个看原始对象里面是什么,都是原始值就可以直接copy过来了;或者,建立了对象以后,挨个判断对象里面的每一个值,看是原始值还是引用值
- 递归
知识点补充——如何区分 数组 和 对象
1.instanceof
可通过 instanceof Array 来判断是否是数组
2.constructor
同样,也可通过construct 判断其构造函数来区分 数组 和 对象
3.toString
这里,我们使用
Object.prototype.toString.call(a)
来实现区分(不是用.toString() )
深度克隆代码实现
var obj = {
name: 'abc',
age: 20,
sex: 'male',
arr: ['aaa', 'bbb', 'ccc', {
qq: 12345,
email: '12345@qq.com'
}]
}
function deepCopy(origin, target) {
var target = target || {};
// 为了防止用户不传 target(容错),给了参数就直接用,不给就当空对象
toStr = Object.prototype.toString;
// 给函数创建一个引用 简化代码
var arrStr = '[object Array]';
// 给该比对字符串创建一个引用 简化代码
// var objStr = '[object Object]';
for (var prop in origin) {
// 先判断是不是原型上的属性,如果是false 就是原型上的属性,而我们不需要拷贝原型上的属性
if (origin.hasOwnProperty(prop)) {
if (typeof (origin[prop]) !== 'null' && typeof (origin[prop]) == 'object') {
// 判断是 引用值 再区分数组和对象
if (toStr.call(origin[prop]) == arrStr) {
// 是数组 创建空数组
target[prop] = [];
} else {
// 是对象 创建空对象
target[prop] = {};
}
deepCopy(origin[prop], target[prop]);
} else {
// 判断是 原始值 直接进行赋值
target[prop] = origin[prop];
}
}
}
return target;
}
var obj1 = deepCopy(obj);
更改前
更改后的obj的arr属性并未被更改