# 了解一下 JS 浅/深克隆??

# 为什么需要克隆,直接赋值不就好了?

js 有两种大类的类型:基本类型(undefined、Null、Boolean、Number 和 String)和引用类型(Object、Array 和 Function),基本类型的复制是不会产生深度复制和浅度复制问题,当使用引用类型进行复制的时候,其实复制的是指向内存值的指针,并没有真实复制其数据(浅复制)。

# 那什么是浅克隆深克隆呢?

  • 浅克隆就是将栈内存中的引用复制一份,赋给一个新的变量,本质上两个指向堆内存中的同一地址,内容也相同,其中一个变化另一个内容也会变化(根本上改变的是同一个对象)。

  • 深克隆就是创建一个新的对象,开辟一块内存,然后将原对象中的数据全部复制过去,新对象和原对象毫无关系,互不影响。

# 如何实现浅克隆?

# 方式一:扩展运算符

const obj1 = {
  ...obj
}
console.log('obj1', obj1)
1
2
3
4

# 方式二:混入

const obj2 = Object.assign({}, obj)
console.log('obj2', obj2)
1
2

当然,还有很多方法,如遍历对象的 key 逐一赋值,这里就不举例了

# 如何实现深克隆?

# 方式一:JSON 转换

const obj3 = JSON.parse(JSON.stringify(obj))
console.log('obj3', obj3)
1
2

JSON 转换可以解决大部分场景,但是它有一些不足:

  • 无法实现对函数、RegExp 等特殊对象的克隆。
  • 会抛弃对象的 constructor,所有的构造函数都会指向 object
  • 对象有循环引用,会报错

# 方式二:MessageChannel

// 解决循环引用和undefined,但不能克隆函数和Symbol
function clone_message_channel(obj) {
  return new Promise((resolve, reject) => {
    const message_channel = new MessageChannel()
    console.log('returnnewPromise ~ message_channel', message_channel)
    const { port1, port2 } = message_channel
    port2.postMessage(obj)
    port1.onmessage = (ev) => resolve(ev.data)
  })
}
// 如何使用
const obj4 = async () => {
  const clone = await clone_message_channel(obj)
  console.log('obj4 ~ clone', clone)
}
obj4()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

同样,messageChannel 虽然解决了循环引用和 undefined 的问题,但是它不能克隆函数和 Symbol

# 方式三:手写

function deep_clone(obj) {
  const map = new WeakMap()
  const _clone = (obj) => {
    if (obj instanceof Object) {
      if (map.has(obj)) {
        // 处理循环引用
        return map.get(obj)
      }
      let newObj = undefined
      if (obj instanceof Function) {
        // 处理函数
        newObj = function() {
          return obj.apply(this, arguments)
        }
      } else if (obj instanceof Array) {
        // 处理数组
        newObj = []
      } else if (obj instanceof RegExp) {
        // 处理正则
        newObj = new RegExp(obj.source, obj.flags)
      } else if (obj instanceof Date) {
        // 处理Date类
        newObj = new Date(obj)
      } else {
        // 处理普通对象
        newObj = {}
      }
      // 处理原型
      newObj.__proto__ = Object.getPrototypeOf(obj)
      // 相当于浅克隆一个对象
      const desc = Object.getOwnPropertyDescriptors(obj)
      const clone = Object.create(Object.getPrototypeOf(obj), desc)
      map.set(obj, clone)
      for (const key of Reflect.ownKeys(obj)) {
        //key为Symbol值
        if (obj.hasOwnProperty(key)) {
          newObj[key] = _clone(obj[key])
        }
      }
      return newObj
    }
    // 原始值、Symbol
    return obj
  }
  return _clone(obj)
}
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
最近更新: 4 小时前