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

结果如下

大功告成~~

写在最后

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

相关推荐
呆头鸭L2 分钟前
用vue3+ts+elementPlus+vite搭建electron桌面端应用
前端·vue.js·electron
aPurpleBerry2 分钟前
React Hooks(数据驱动、副作用、状态传递、状态派生)
前端·react.js·前端框架
IT_陈寒3 分钟前
2025年React生态最新趋势:我从Redux迁移到Zustand后性能提升40%的心得
前端·人工智能·后端
前端小臻3 分钟前
react没有双向数据绑定是怎么实现数据实时变更的
前端·javascript·react.js
困惑阿三4 分钟前
CSS 动效交互实验室
前端·css
哟哟耶耶10 分钟前
随笔小计-前端经常接触的http响应头(跨域CORS,性能-缓存-安全,token)
前端·网络协议·http
Allen_LVyingbo13 分钟前
病历生成与质控编码的工程化范式研究:从模型驱动到系统治理的范式转变
前端·javascript·算法·前端框架·知识图谱·健康医疗·easyui
rgeshfgreh18 分钟前
Python函数全解析:定义、参数与作用域
前端·数据库·python
Serendipity-Solitude24 分钟前
使用HTML创建井字棋
前端·html