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

结果如下

大功告成~~

写在最后

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

相关推荐
鹏多多7 分钟前
React动画方案对比:CSS动画和Framer Motion和React Spring
前端·javascript·react.js
亿元程序员10 分钟前
8年游戏主程,一篇文章,多少收益?
前端
IT_陈寒25 分钟前
5个Java 21新特性实战技巧,让你的代码性能飙升200%!
前端·人工智能·后端
咖啡の猫27 分钟前
Vue内置指令与自定义指令
前端·javascript·vue.js
昔人'34 分钟前
使用css `focus-visible` 改善用户体验
前端·css·ux
前端双越老师37 分钟前
译: 构建高效 AI Agent 智能体
前端·node.js·agent
艾小码1 小时前
告别数据混乱!掌握JSON与内置对象,让你的JS代码更专业
前端·javascript
liangshanbo12156 小时前
写好 React useEffect 的终极指南
前端·javascript·react.js
哆啦A梦15888 小时前
搜索页面布局
前端·vue.js·node.js