当前位置:   article > 正文

【JavaScript】js实现深拷贝的方法_js messagechannel

js messagechannel

前言

在js中我们想要实现深拷贝,首先要了解深浅拷贝的区别。
浅拷贝:只是拷贝数据的内存地址,而不是在内存中重新创建一个一模一样的对象(数组)
深拷贝:在内存中开辟一个新的存储空间,完完全全的拷贝一整个一模一样的对象(数组)

1. MessageChannel实现深拷贝

MDN资料:https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel

MDN中关于MessageChannel的介绍

Channel Messaging APIMessageChannel 接口允许我们创建一个新的消息通道,并通过它的两个 MessagePort 属性发送数据。

备注: 此特性在 Web Worker 中可用

使用方法:

封装一个deepclone函数

function deppClone(obj) {
	return new Promise(resolve => {
		const { port1, port2 } = new MessageChannel()
		port1.postMessage(obj)
		port2.onmessage = e => {
			resolve(e.data)
		}
	})
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

测试代码:

function deppClone(obj) {
	return new Promise(resolve => {
		const { port1, port2 } = new MessageChannel()
		port1.postMessage(obj)
		port2.onmessage = e => {
			resolve(e.data)
		}
	})
}
const obj = { a: "", c: undefined, e: 0, f: [], g: NaN, h: null }
obj.b = obj

let newObj = null
await deppClone(obj).then(res => {
  newObj = res
})

console.log("obj", obj)
console.log("newObj", newObj)
console.log(obj === newObj)
console.log(obj.b === newObj)
console.log(obj === newObj.b)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

结果:

在这里插入图片描述

优点:

他可以完美解决循环引用的问题。
支持的浏览器版本比较多 (文献资料

缺点:

不支持拷贝函数
不支持拷贝Symbol

2. structuredClone实现深拷贝

文献资料:https://developer.mozilla.org/zh-CN/docs/web/api/structuredClone

MDN中关于structuredClone()的介绍

全局的 structuredClone() 方法使用结构化克隆算法将给定的值进行深拷贝。

该方法还支持把原始值中的可转移对象转移到新对象,而不是把属性引用拷贝过去。 可转移对象与原始对象分离并附加到新对象;它们不可以在原始对象中访问被访问到。

使用方法:

structuredClone(obj)
  • 1

测试代码:

const obj = { a: "",c:undefined,e:0,f:[],g:NaN,h:null }
obj.b = obj

const newObj = structuredClone(obj)

console.log('obj',obj)
console.log('newObj',newObj)
console.log(obj === newObj)
console.log(obj.b === newObj)
console.log(obj === newObj.b)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

结果:

在这里插入图片描述

优点:

他可以完美解决循环引用的问题。
而且非常简单,调用API即可。

缺点:

这是一个新的API,它支持的浏览器版本比较新
不支持拷贝函数
不支持拷贝Symbol

在这里插入图片描述

3.手动封装deepClone函数

最经典的就是手动封装一个deepClone函数,去主动判断传入类型并且递归创建新的对象

使用方法:

封装函数,并判断类型

function isObject(value) {
  const valueType = typeof value
  return (value !== null) && (valueType === "object" || valueType === "function")
}



function deepClone(originValue, map = new WeakMap()) {
  // 判断是否是一个Set类型
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }

  // 判断是否是一个Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }

  // 判断如果是Symbol的value, 那么创建一个新的Symbol
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description)
  }

  // 判断如果是函数类型, 那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue
  }

  // 判断传入的originValue是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue
  }
  if (map.has(originValue)) {
    return map.get(originValue)
  }

  // 判断传入的对象是数组, 还是对象
  const newObject = Array.isArray(originValue) ? []: {}
  map.set(originValue, newObject)
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key], map)
  }

  // 对Symbol的key进行特殊的处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const sKey of symbolKeys) {
    // const newSKey = Symbol(sKey.description)
    newObject[sKey] = deepClone(originValue[sKey], map)
  }
  
  return newObject
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

测试代码:

let s1 = Symbol("aaa")
let s2 = Symbol("bbb")

const obj = {
  a: 18,
  b: {
    c: "www",
    d: {
      e: "www"
    }
  },
  // 数组类型
  hobbies: ["abc", "cba", "nba"],
  // 函数类型
  foo: function(m, n) {
    console.log("wwww")
    console.log("wwww")
    return 123
  },
  // Symbol作为key和value
  [s1]: "abc",
  s2: s2,
  // Set/Map
  set: new Set(["aaa", "bbb", "ccc"]),
  map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}

obj.info = obj

const newObj = deepClone(obj)
console.log(newObj === obj)
console.log('obj',obj)
console.log('newObj',newObj)
console.log(newObj.s2 === obj.s2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

结果:

在这里插入图片描述

优点:

他可以完美解决各种问题。

4. JSON实现深拷贝

这是最不推荐使用的方法

优点:这是最简单的方式了,只能处理不复杂的对象

const symbol1 = Symbol();
const obj = { a: "",c:undefined,e:0,f:[],g:NaN,h:null,i:symbol1 }

let newObj = JSON.parse(JSON.stringify(obj))

console.log('obj',obj)
console.log('newObj',newObj)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

缺点:

不能解决循环引用
在这里插入图片描述
NaN问题

在这里插入图片描述

忽略Symbol(),undefined

在这里插入图片描述

5.其他

比如lodash、jQuery等插件的实现方式就不多讲了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/68372
推荐阅读
相关标签
  

闽ICP备14008679号