1. ES5 来实现对象的深拷贝
- 详细注释
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
var obj = {
name: 'zhangbing',
age: 10,
info: {province: '湖北省', university: 'CUC', classNo: 3, hobbies: ['唱歌', '读书', '篮球', {a: 1}]}
}
// params:
// origin: 待拷贝的对象
// target : 拷贝后产生的新对象
function deepClone(origin, target) {
var tar = target || {} // target 参数如果没传的话默认为空对象
var toStr = Object.prototype.toString
var arrType = '[object Array]' // Object.prototype.toString([]) === '[object Array]'
// 遍历待对象的每一项
for (var k in origin) {
// 首先判断属性是否位于对象本身上,不拷贝原型上的属性
if (origin.hasOwnProperty(k)) {
// 遍历的项如果为对象且不为null(因为 typeof null === ’object')
if (typeof origin[k] === 'object' && origin[k] !== null) {
// 遍历的项如果为数字总用【】来包裹其进一步拷贝的内容
// 否则遍历的项为对象,则使用{}来包裹其进一步拷贝的内容
tar[k] = toStr.call(origin[k]) === arrType ? [] : {}
// 递归进行内层的深拷贝
deepClone(origin[k], tar[k])
} else {
// 如果遍历项为值类型,直接赋值复制
tar[k] = origin[k]
}
}
}
// 返回深拷贝得到的新对象
return tar
}
// 测试深拷贝代码的效果
const newObj = deepClone(obj, {})
newObj.info.hobbies[3].a = 3
console.log(obj)
console.log(newObj)
</script>
</body>
</html>
- 控制台输出结果:
2. WeakMap 知识铺垫
WeakMap的引入可以解决当按钮节点被删除后,绑定的事件处理函数需要设置为null才能被垃圾回收机制清除掉的问题。 因为WeakMap 的键名是弱引用,将事件处理函数设置为键值可以弥补手动设置为null的问题,当节点移除后事件处理函数也会自动清除掉。
3. ES6的方法实现深拷贝(引入WeakMap)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
function deepClone(origin, hashMap = new WeakMap()) {
// 当 origin 为null/undefined 或者为值类型时,直接返回origin
if (origin == undefined || typeof origin !== 'object') {
return origin
}
// 当 origin 为 Date 类型时, 返回Date类型的数据
if (origin instanceof Date) {
return new Date(origin)
}
// 当 origin 为 RegExp 类型时, 返回 RegExp 类型的数据
if (origin instanceof RegExp) {
return new RegExp(origin)
}
// 找到hashKey,看当前传进来的 origin 的 hashKey 是否存在
const hashKey = hashMap.get(origin)
// 存在就直接return
if (hashKey) {
return hashKey
}
// 当origin为数组时,target初始化为[]; 当origin为对象时,target初始化为{}
const target = new origin.constructor()
hashMap.set(origin, target)
for (let k in origin) {
if (origin.hasOwnProperty(k)) {
target[k] = deepClone(origin[k], hashMap)
}
}
return target
}
// var obj = {
// name: 'zhangbing',
// age: 10,
// info: {province: '湖北省', university: 'CUC', classNo: 3, hobbies: ['唱歌', '读书', '篮球', {a: 1}]}
// }
// const newObj = deepClone(obj)
// newObj.info.hobbies[3].a = 3
// console.log(obj)
// console.log(newObj)
// 可以看到成功实现了深拷贝
let test1 = {}
let test2 = {}
test2.test1 = test1 //
test1.test2 = test2
console.log(deepClone(test2)) // Uncaught RangeError: Maximum call stack size exceeded
// 可以看到存在循环引用的问题 ,程序会报错。
// 这个问题想到使用Map来解决,也就是hash
</script>
</body>
</html>