# 浅深拷贝
# 浅拷贝
function cloneShallow(source) {
var target = {};
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
return target;
}
# 实现 Object.assign
if (typeof Object.assign2 != 'function') {
// 原生情况下挂载在 Object 上的属性是不可枚举的,但是直接在 Object 上挂载属性 a 之后是可枚举的,所以不使用Object.assign2 = function...
// 这里必须使用 Object.defineProperty,并设置 enumerable: false 以及 writable: true, configurable: true。默认情况下不设置就是false
Object.defineProperty(Object, 'assign2', {
value: function(target) {
'use strict';
if (target == null) {
// 判断参数是否正确
throw new TypeError('Cannot convert undefined or null to Object');
}
// 原始类型被包装为对象
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
// 判断参数是否正确
if (nextSource != null) {
// in 操作符会检查属性是否在对象及其 [[Prototype]] 原型链中
// hasOwnProperty(..) 只会检查属性是否在 myObject 对象中,不会检查 [[Prototype]] 原型链
for (var key in nextSource) {
// 直接使用 myObject.hasOwnProperty(..) 是有问题的,因为有的对象可能没有连接到 Object.prototype
// 上(比如通过 Object.create(null) 来创建),这种情况下,使用 myObject.hasOwnProperty(..) 就会失败。
if (Object.prototype.hasOwnProperty.call(nextSource, key)) {
to[key] = nextSource[key];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
# 深拷贝
// 使用哈希表解决循环引用问题:
// 1、WeakMap只接受对象作为键名(null除外),且cloneDeep会先判断source是否是对象,所以满足条件
// 2、WeakMap的键名所指向的对象,不计入垃圾回收机制
function cloneDeep(source, hash = new WeakMap()) {
// 判断是否是对象
function isObject(obj) {
// 除了普通对象还有数组,日期,函数等也都应该是可以拷贝的对象,所以不用下面一行的方法
// return Object.prototype.toString.call(obj) === '[object Object]';
return typeof obj === 'object' && obj != null;
}
// 非对象返回自身
if (!isObject(source)) {
return source;
}
// 查hash表,如果这个source已经被引用过了,直接返回
// 即循环引用时,深节点又指向source对象,则直接返回哈希表记录的值
if (hash.has(source)) {
return hash.get(source);
}
// 区分拷贝数组和普通对象
var target = Array.isArray(source) ? [] : {};
// 1、哈希表记值,键为source,值为target。
// 2、使用hash表同时还可以解决引用丢失问题:
// 比如source中有2个key的值指向同一个对象source[key],不使用hash表拷贝后,每个key的值都是2个地址不相同的新对象
// 使用哈希表后,因为source[key]被记录了,拷贝第二个值时会通过在哈希表查source[key]找到target直接返回第一个保存的target值
hash.set(source, target);
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep(source[key]);
} else {
target[key] = source[key];
}
}
}
}
// 这个实现还存在一个问题:递归爆栈
// 递归时函数执行上下文会一直被推入栈中,如果递归次数过多,最终会导致栈溢出问题
// 解决递归爆栈可以使用循环,在循环中使用广度优先遍历各个节点
写的不对,也不想写了,看别人的吧:https://juejin.cn/post/6844903929705136141 (opens new window)