浅拷贝
定义:只把第一级
的拷贝一份,赋值给新数组,一般我们实现数组克隆的方法都是
方法:slice,concat,[…ary] 等
var a = { name: 1 };
var arr = [a];
var temp = arr.slice();
console.log(temp); // [{name:2}] 为什么这里name不是 1,控制台的异步
a.name = 2;
console.log(temp); //[{name:2}]
深拷贝
定义:把每一级
都克隆一份给新数组.
使用 JSON
实现原理:
JSON.stringify(arr) 先把原始对象变为字符串(去除堆和嵌套关系)
JSON.parse(…) 再把字符串转换为新对象,重新开辟内存存储
存在问题:
undefined
,函数
,symbol
会被忽略- Number,String,Boolean类型会被转成初始值
- 不可枚举属性会被忽略
- 正则变成空对象,日期对象变成字符串
var a = {
name: 1,
f: () => "Fn",// 会被忽略
u: undefined,// 会被忽略
s: Symbol.for("s"),// 会被忽略
num:new Number(1),//会被转成初始值
str: new String("1"),//会被转成初始值
bool: new Boolean("1"),//会被转成初始值
reg: /a+/g,// 会被转成空对象
d: new Date(),//会被转成字符串
};
Object.defineProperty(a, "e", {
value: 1,
enumerable: false
})
var arr = [a];
var temp = JSON.parse(JSON.stringify(arr));
a.name = 2;
console.log(temp);
// [
// {
// name: 1,
// num: 1,
// str: '1',
// bool: true,
// reg: {},
// d: '2023-04-21T01:26:46.845Z'
// }
// ]
递归实现深拷贝
实现思路
- 考虑 null,基本类型
- 考虑 Date,正则对象
- 考虑是否为自己的私有属性
- 递归
function clone(obj) {
if (obj === null) return obj
if(typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
const temp = new obj.constructor()
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break
temp[key] = clone(obj[key])
}
return temp
}
var a = {
name: 1,
f: () => "Fn",
u: undefined,
s: Symbol.for("s"),
num: new Number(1),
str: new String("1"),
bool: new Boolean("1"),
reg: /a+/g,
d: new Date(),
};
Object.defineProperty(a, "e", {
value: 1,
enumerable: false
})
let res = clone({a})
a.name = 3
console.log(res)
// {
// a: {
// name: 1,
// f: [Function: f],
// u: undefined,
// s: Symbol(s),
// num: [Number: 0],
// str: [String: ''] { '0': '1' },
// bool: [Boolean: false],
// reg: /a+/g,
// d: 2023-04-23T06:49:38.432Z
// }
// }
实现思路
- 考虑 Symbol 属性
- 考虑 循环引用 造成的内存泄漏(weakMap)
- 考虑 对象属性 和 继承原型
function clone(obj,hash = new WeakMap()) {
if (obj === null) return obj
if(typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 处理循环引用
if(hash.has(obj))return hash.get(obj)
// 处理描述属性
let allDesc = Object.getOwnPropertyDescriptor(obj)
// 处理原型
let temp = Object.create(Object.getPrototypeOf(obj),allDesc)
hash.set(obj,temp)
for (let key of Reflect.ownKeys(obj)) {
temp[key] = clone(obj[key],hash)
}
return temp
}
Object.prototype.aaa = 1
let obj = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: { name: '我是一个对象', id: 1 },
arr: [0, 1, 2],
func: function () { console.log('我是一个函数') },
date: new Date(0),
reg: new RegExp('/我是一个正则/ig'),
[Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性
let res = clone(obj)
console.log(res)
// {
// num: 0,
// str: '',
// boolean: true,
// unf: undefined,
// nul: null,
// obj: { name: '我是一个对象', id: 1 },
// arr: Array { '0': 0, '1': 1, '2': 2, length: 3 },
// func: [Function: func],
// date: 1970-01-01T00:00:00.000Z,
// reg: /\/我是一个正则\/ig/,
// loop: [Circular *1],
// [Symbol(1)]: 1
// }
浅比较
实现思路:
- 先比较成员的数量
- 在逐一比较内部成员(只比较第一层)
- 比较内部成员,是否都有这个成员,且是否相同(考虑 NaN )
const shallowEqual = function (objA, objB) {
if (!isObject(objA) || !isObject(objB)) return false
if (objA === objB) return true
// 先比较成员的数量
let keysA = Reflect.ownKeys(objA)
let keysB = Reflect.ownKeys(objB)
if (keysA.length !== keysB.length) return false
// 逐一比较内部成员(值比较第一层)
for (let i = 0; i < keysA.length; i++) {
let key = keysA[i];
// 如果一个对象中有这个成员,一个对象中没有;或者,都有这个成员,但是成员值不一样,都应该被判定为为不相同
if (!objB.hasOwnProperty(key) || !Object.is(objA[key], objB[key])) {
return false
}
}
return true
function isObject(obj) { // 监测是否为对象
return obj !== null && /^(object|function)$/.test(typeof obj)
}
}