[深度克隆]惊呆面试官(doge)!!优雅的对象克隆方式

各位IT界的彦祖,神仙姐姐们中午好。

前提

克隆方式在日常开发中大家都还是使用较多的,毕竟像数组、对象都是引用类型,直接使用一不小心就会造成意料之外的bug。那么今天我们来分享一下对象的克隆方式通常有哪些。

正文

回忆一下,大家在日常开发时需要克隆的时候通常使用什么方式呢,有人会说使用进行转换JSON.parse(JSON.stringify(xxx)),也有人说使用lodash/cloneDeep,更甚者自己封装一段递归方式去实现。相对来说JSON转换是最简单的,但是它不够完美,有一些场景是不能正常转换的。如下:

js 复制代码
<script>
    const obj1 = { a: 'a', b: 'b' } // 正常对象
    const obj2 = { a: 'a', b: [1,2] } // 包含数组对象
    const obj3 = { a: 'a', func: function(){} }  // 函数对象
    const obj4 = { a: 'a', o: { inner: 'inner' } } // 对象对象
    const obj5 = { a: 'a', map: new Map() } // map 对象
    const obj6 = { a: 'a', set: new Set() } // Set 对象
    console.log(JSON.parse(JSON.stringify(obj1))); 
    console.log(JSON.parse(JSON.stringify(obj2))); 
    console.log(JSON.parse(JSON.stringify(obj3))); 
    console.log(JSON.parse(JSON.stringify(obj4))); 
    console.log(JSON.parse(JSON.stringify(obj5))); 
    console.log(JSON.parse(JSON.stringify(obj6))); 
    obj1.c = obj1  // 循环引用对象
    console.log(JSON.parse(JSON.stringify(obj1))); 
  </script>

结果: 我们可以清晰的看到,当对象中包含这些场景时,JSON转换是不正确的

  • 对象中存在 function 时,function 会无法转换
  • 对象中存在 Map | Set 时,会将 Map | Set 转换为普通对象
  • 对象存在循环引用时,那么会直接报错导致程序终止 当然正常情况下,使用JSON转换是没太大问题的,也足以应对大多数情况。

优雅的克隆方式

它是一个构造函数 MessageChannel,通过该构造函数,我们可以实例化一个消息通道,实例中包含了两个属性(port1, port2),表示两个端点,这两个端点之间可以相互传递消息。

js 复制代码
<script>
    const { port1, port2 } = new MessageChannel();
    port1.postMessage('MessageChannel')
    port2.onmessage = mes => {
      console.log(mes);
    }
  </script>

可以看到我们传递过去的内容存在于 mes.data 属性中,现在我们只是传递字符串,那么我们可以传递对象过去。

js 复制代码
const { port1, port2 } = new MessageChannel();
const obj1 = { a: 'a', b: 'b' } // 正常对象
const obj2 = { a: 'a', b: [1,2] } // 包含数组对象
const obj3 = { a: 'a', o: { inner: 'inner' } } // 对象对象
const obj4 = { a: 'a', map: new Map() } // map 对象
const obj5 = { a: 'a', set: new Set() } // Set 对象
port1.postMessage(obj1)
port1.postMessage(obj2)
port1.postMessage(obj3)
port1.postMessage(obj4)
port1.postMessage(obj5)
obj1.c = obj1  // 循环引用对象
port1.postMessage(obj1)
port2.onmessage = mes => {
  console.log(mes.data);
  console.log(obj1 === mes.data);
}

可以清楚地看到,传递过去的对象都能完美的克隆下来,包括 Map | Set 循环引用等,但是注意函数对象是不能使用的,会报错。那么接下来我们就可以将其封装为一个函数。

js 复制代码
const obj = { a: 'a', map: new Map() } // map 对象
function cloneDeep(obj){
  const { port1, port2 } = new MessageChannel();
  port1.postMessage(obj)
  port2.onmessage = mes => {
    console.log(mes.data);
    console.log(obj === mes.data);
  }
}
cloneDeep(obj)

这时我们发现怎么把 mes.data给它 return 出去呢,聪明的彦祖和仙女姐姐们应该想到了, 对!没错。就是Promise。将其改为

js 复制代码
const obj = { a: 'a', map: new Map() } // map 对象
function cloneDeep(obj){
  return new Promise((resolve, reject) => {
    const { port1, port2 } = new MessageChannel();
    port1.postMessage(obj)
    port2.onmessage = mes => {
      resolve(mes.data)
    }
  })
}
cloneDeep(obj).then(res => {
  console.log('res', res);
})

结果如下

大功告成~~

写在最后

当然我们还可以再优化一下,例如做一下异常处理,避免其他人使用时传递了函数对象过来。 这不是最简单的对象克隆方式,但在面试中,如果考察到对象克隆,这无疑是最惊艳面试官的实现方式之一。此外,在日常开发中也能使用到该克隆方式。 再见啦~~

相关推荐
JustHappy5 小时前
古法编程秘籍(二):什么是代码模块化?别背概念,把房间收拾明白就够了
前端·后端
小江的记录本5 小时前
【JVM虚拟机】堆内存分代模型:年轻代(Eden+Survivor)、老年代、元空间Metaspace(附《思维导图》+《面试高频考点清单》)
java·前端·jvm·后端·python·spring·面试
weixin_471383035 小时前
图片预解码缓存
前端·浏览器缓存·图片预解码
郑洁文7 小时前
基于网络爬虫的Web敏感信息泄露自动化检测工具
前端·爬虫·网络安全·自动化
郑洁文7 小时前
可视化Web渗透分析工具的设计与实现
前端
罗超驿8 小时前
18.Web API 实战:元素与表单属性的获取和修改
开发语言·前端·javascript
边界条件╝8 小时前
微前端进阶(四)
前端·状态模式
无风听海8 小时前
JSON Web Token(JWT)完全指南
java·前端·json
IT_陈寒8 小时前
Python闭包里藏的这个坑,差点让我加班到凌晨
前端·人工智能·后端
IT_陈寒8 小时前
Java注解空指针?这个坑我踩得莫名其妙
前端·人工智能·后端