[深度克隆]惊呆面试官(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);
})

结果如下

大功告成~~

写在最后

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

相关推荐
子兮曰2 小时前
OpenClaw架构揭秘:178k stars的个人AI助手如何用Gateway模式统一控制12+通讯频道
前端·javascript·github
百锦再3 小时前
Reactive编程入门:Project Reactor 深度指南
前端·javascript·python·react.js·django·前端框架·reactjs
莲华君3 小时前
React快速上手:从零到项目实战
前端·reactjs教程
百锦再3 小时前
React编程高级主题:测试代码
android·前端·javascript·react.js·前端框架·reactjs
易安说AI3 小时前
Ralph Loop 让Claude无止尽干活的牛马...
前端·后端
失忆爆表症5 小时前
05_UI 组件库集成指南:Shadcn/ui + Tailwind CSS v4
前端·css·ui
小迷糊的学习记录5 小时前
Vuex 与 pinia
前端·javascript·vue.js
发现一只大呆瓜5 小时前
前端性能优化:图片懒加载的三种手写方案
前端·javascript·面试
不爱吃糖的程序媛5 小时前
Flutter 与 OpenHarmony 通信:Flutter Channel 使用指南
前端·javascript·flutter
利刃大大5 小时前
【Vue】Element-Plus快速入门 && Form && Card && Table && Tree && Dialog && Menu
前端·javascript·vue.js·element-plus