浅克隆
只把第一级克隆一份过来,第二级及以后和原始对象公用相同的堆地址
slice 实现
在 Array 中的 slice 和 concat 方法,不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = arr1.slice(0);
console.log(arr1 === arr2); // false
console.log(arr1[2] === arr2[2]); // true
concat 实现
在 Array 中的 slice 和 concat 方法,不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = arr1.concat([]);
console.log(arr1 === arr2); // false
console.log(arr1[2] === arr2[2]); // true
展开运算符实现
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = [...arr1];
console.log(arr1 === arr2); // false
console.log(arr1[2] === arr2[2]); // true
Object.assign() 实现
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = Object.assign([], arr1);
console.log(arr1);
console.log(arr2);
// [ 10, 20, { name: '旺旺' }, /^\d+$/, [Function] ]
// [ 10, 20, { name: '旺旺' }, /^\d+$/, [Function] ]
arr1[2].name = "张三"
console.log(arr1);
console.log(arr2);
// [ 10, 20, { name: '张三' }, /^\d+$/, [Function] ]
// [ 10, 20, { name: '张三' }, /^\d+$/, [Function] ]
jQuery 中的 $.extend
在 jQuery 中
$.extend(deep, target, object1, objectN)
方法可以进行深浅拷贝,各参数说明如下:
- deep:如过设为 true 为深拷贝,默认是 false 浅拷贝;
- target:要拷贝的目标对象;
- object1:待拷贝到第一个对象的对象;
- objectN:待拷贝到第 N 个对象的对象;
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = [];
/* arr1 合并到 arr2 中 */
$.extend(false, arr2, arr1);
console.log(arr2);
</script>
</body>
深克隆
基于 JSON 方法实现
局限性:
- 不能复制 函数、undefined、Symbol、BigInt
- 不能处理循环引用
- 丢失原型链
- 不能正确处理 Date、RegExp、Map、Set 等特殊对象
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = JSON.parse(JSON.stringify(arr1));
// [ 10, 20, { name: '旺旺' }, {}, null ]
// 正则变成 {}
// 函数变成 null
console.log(arr2);
jQuery 中的 $.extend
在 jQuery 中
$.extend(deep, target, object1, objectN)
方法可以进行深浅拷贝,各参数说明如下:
- deep:如过设为 true 为深拷贝,默认是 false 浅拷贝;
- target:要拷贝的目标对象;
- object1:待拷贝到第一个对象的对象;
- objectN:待拷贝到第 N 个对象的对象;
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/, function () { }];
let arr2 = [];
/* arr1 合并到 arr2 中 */
$.extend(true, arr2, arr1); // 第一个参数为 true 为深度合并
console.log(arr2);
</script>
</body>
递归实现
function _type(value) {
return Object.prototype.toString.call(value);
}
// WeakMap 弱连接,key 必须是对象,这意味着,如果没有其他引用指向一个对象,那么这个对象可以被垃圾回收器回收,而不需要手动从 WeakMap 中删除
function _deepClone(obj, hash = new WeakMap()) {
if (obj == null) return obj; // null、undefined 直接返回
if (typeof obj !== "object") return obj; // 基本数据值、函数 直接返回
if (_type(obj) === "[object RegExp]") return new RegExp(obj); // 正则
if (_type(obj) === "[object Date]") return new Date(obj); // 日期
// 对象(object、array、......) 放入 WeakMap 备忘录
if (hash.has(obj)) return hash.get(obj);
// obj.constructor:找到的是所属类原型上的 constructor,保证传递进来什么类型的值,最后创建的 newObj 也是对应类型的实例
let newObj = new obj.constructor();
// 存储在备忘录中,避免重复克隆
hash.set(obj, newObj);
// 遍历数组和对象
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break;
// 递归判断每一个属性值
newObj[key] = _deepClone(obj[key], hash);
}
return newObj;
}
// 测试
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/];
arr1.push(arr1); // 循环引用
console.log(deepClone(arr1) === arr1); // false
MessageChannel 实现
使用 MessageChannel 实现的深拷贝只能解决 undefined 和 循环引用对象 的问题,对于 Symbol 和 function 依然束手无策;
function deepClone(obj) {
return new Promise((resolve) => {
const { port1, port2 } = new MessageChannel();
port1.postMessage(obj);
port2.onmessage = (msg) => {
resolve(msg.data);
};
});
}
// 测试
let arr1 = [10, 20, { name: '旺旺' }, /^\d+$/];
arr1.push(arr1); // 循环引用
console.log(deepClone(arr1) === arr1); // false
第 2️⃣ 座大山:封装、继承、多态
上一篇