浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝:会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝的实现方式
直接赋值一个变量
let obj = {username: 'kobe', age: 39, sex: {option1: '男', option2: '女'}};
let obj1 = obj;
obj1.sex.option1 = '不男不女'; // 修改复制的对象会影响原对象
console.log(obj1, obj);
Object.assign()
let obj = {
username: 'kobe'
};
let obj2 = Object.assign(obj);
obj.username = 'wade';
console.log(obj2);//{username: "wade"}
Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);
修改新对象会改到原对象:
Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);
同样修改新对象会改到原对象:
关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。详细规则请看MDN对应函数讲解。
深拷贝的实现方式
只有值类型数据的深拷贝
针对只有值的数据对象,下面一行代码足以!
JSON.parse(JSON.stringify(obj))
原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。这种方法只能处理只有值类型数据的拷贝。
不严谨的简单的深拷贝
function clone(source) {
var target = {};
for(var i in source) {
if (source.hasOwnProperty(i)) {
//通过hasOwnProperty方法来进行筛选,所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
if (typeof source[i] === 'object') {
target[i] = clone(source[i]); // 注意这里
} else {
target[i] = source[i];
}
}
}
return target;
}
问题存在:
- 没有对参数做检验
- 判断是否对象的逻辑不够严谨
- 没有考虑数组的兼容
进阶深拷贝
function isObj(obj)
{
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj)
{
let tempObj = Array.isArray(obj) ? [] :{};
for(let key in obj)
{
tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key];
}
return tempObj;
}
问题存在:
- __拷贝环__,也就是对 对象循环引用 的拷贝出现问题
对象环知识点补充
方法一 用try catch的捕获异常的方法来判断,代码简洁
function cycleDetector (obj) {
console.log(arguments)
// 请添加代码
let result = false;
try {
JSON.stringify(obj);
} catch (e) {
result = true;
} finally {
return result;
}
}
方法二 时间更快,但是它执行递归,逻辑较第一种更复杂,空间也需要更大
function cycleDetector2(obj) {
let hasCircle = false,
cache = [];
(function(obj) {
Object.keys(obj).forEach(key => {
const value = obj[key]
if (typeof value == 'object' && value !== null) {
const index = cache.indexOf(value)
if (index !== -1) {
hasCircle = true
return
} else {
cache.push(value)
arguments.callee(value)
// (注:箭头函数没有arguments对象,此时的arguments指向该匿名函数的参数)
}
}
})
})(obj)
return hasCircle
}
针对环的深拷贝
可以使用一个WeakMap结构存储已经被拷贝的对象,每一次进行拷贝的时候就先向WeakMap查询该对象是否已经被拷贝,如果已经被拷贝则取出该对象并返回,将deepCopy函数改造成如下:
function isObj(obj)
{
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {
if(hash.has(obj)) return hash.get(obj)
let cloneObj = Array.isArray(obj) ? [] : {}
hash.set(obj, cloneObj)
for (let key in obj) {
cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
}
return cloneObj
}
问题存在:
- 没有考虑对new Date(),正则,函数类型的对象的拷贝
结合环,针对date,reg,箭头函数类型的深拷贝
const obj = { arr: [111, 222], obj: {key: '对象'}, a: () => {console.log('函数')}, date: new Date(), reg: /正则/ig}
function isObj(obj)
{
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {
let cloneObj;
let Constructor = obj.constructor;
switch(Constructor){
case RegExp:
cloneObj = new Constructor(obj)
break;
case Date:
cloneObj = new Constructor(obj.getTime())
break;
case Function:
cloneObj = eval(obj.toString());
break;
default:
if(hash.has(obj)) return hash.get(obj)
cloneObj = new Constructor()
hash.set(obj, cloneObj)
}
for (let key in obj) {
cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
}
return cloneObj;
}
const cloneObj = deepCopy(obj);
console.log(cloneObj);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。