面试官:除了JSON.parse(JSON.stringify())和手写一个递归,还有什么办法能实现深拷贝

94 阅读2分钟

一般被问到如何实现深拷贝的时候,我都会回答JSON.parse(JSON.stringify()),然后介绍它的缺点,比如undefinedfunction会被忽略,日期类型会被转为字符串,正则会被转为空对象。遇到循环引用的话被直接报错。所以更加保险的方法是利用递归实现一个深拷贝。一段简简单单的前端八股文送给面试官。今天这个面试官来头不小,问我除了这两种方法还有其他方法实现吗?我当时心想:你是闰土吗,在这找猹吗?你这么问,迅哥儿知道吗?今天就来总结几种方法来实现深拷贝。

postMessage

平常我们在A页面使用iframe标签引入B页面时需要使用postMessage进行两个页面的通信,这里的对象也是进行的深拷贝,且不会像JSON.parse(JSON.stringify(objA))对有些熟悉无法处理,甚至报错。处理逻辑就是将A页面需要深拷贝的对象发送个B页面,B页面将这个对象原封不动的传回来即可。 A页面的代码

<div>A页面</div>
<button onclick="test()">测试</button>
<iframe src="./b.html" frameborder="0" id="bIframe"></iframe>

<script>
  let objA = {
    a: 1,
    b: {
      c: 2
    },
    c: null,
    d: undefined,
    f: /a/,
    g: new Date(),
    i: NaN
  }
  objA.j = objA
  const bIframe = document.getElementById('bIframe')
  
  window.onmessage = function(event){
    let objB = event.data.objA
    objB.b.c = 33333
    console.log(objA)
    console.log(objB)
  }
  function test() {
    bIframe.contentWindow.postMessage({objA}, "*");
  }
</script>

B页面代码

<div>B页面</div>
<script>
  let objB = {a: 33}
  window.onmessage = function(event){
    objB = event.data
    window.parent.postMessage(event.data, "*");
  }
  
</script>

运行结果如下

微信图片_20230220171454.png

可以看到objB的值修改没有影响objA同时特殊类型的值也能成功拷贝,循环引用也不会报错

Worker

原本web worker就是在web中使用的worker,这个worker是独立于web主线程的,在后台运行的线程。比如你的页面要加载一个业务比较耗时的js,你可以让worker单独开一个线程在后台处理,处理完了把结果返回给你,这样就不会阻塞主进程了。它也提供了一些通信方式,这些数据的传递也是深拷贝的。代码如下 A.html

<body>
  <button onclick=" ()">测试</button>
  <script>
    let objA = {
      a: 1,
      b: {
        c: 2
      },
      c: null,
      d: undefined,
      f: /a/,
      g: new Date(),
      i: NaN
    }
    objA.j = objA
    let objB

    function test() {
      const work = new Worker('./worker.js');
      work.postMessage({
        objA
      });
      work.onmessage = function(event) {
        objB = event.data.objA
        objB.b.c = 333
        console.log(objA);
        console.log(objB);
      }
    }
  </script>
</body>

worke.js

self.onmessage = function(event) {
    self.postMessage(event.data);
}

indexDB

在HTML5的时候,新增了一种数据库名为indexDB,这种数据库是存证在客户端本地的NOSQL数据库,这种数据库也可以存储对象。利用它我们也可以实现深拷贝

<body>
  <button id="add" onclick="add()">添加数据</button>
  <button id="get" onclick="get()">获取数据</button>
  <script>
    let objA = {
      a: 1,
      b: {
        c: 2
      },
      c: null,
      d: undefined,
      f: /a/,
      g: new Date(),
      i: NaN
    }
    objA.j = objA
    let objB
    var request = indexedDB.open("copyDB", 1)
    request.onupgradeneeded =function(event){
      const db = request.result;
      const store = db.createObjectStore("Copy", {
          keyPath: "id",
          autoIncrement: true,
        });
      
    };
    request.onsuccess = () => {
        // 获取数据库连接
        const db = request.result
        // 创建事务对象
        const tx = db.transaction("Copy", "readwrite")
        // 创建一个与我们存储的事务
        const copyStore = tx.objectStore("Copy")
        
    }
    
    function add() {
        // 设置一个事务
        const db = request.result
        const tx = db.transaction("Copy", "readwrite")
        const copyStore = tx.objectStore("Copy")
        copyStore.put({objA}) // 添加到 indexedDB
    }
    function get() {
      var request=indexedDB.open("copyDB");
      request.onsuccess=function(){
        var db=request.result;
        var transaction=db.transaction("Copy","readonly");
        var objStore=transaction.objectStore("Copy");
        //从对象存储空间中读取数据
        var result=objStore.get(1);
        
        result.onsuccess=function(){
          objB = result.result.objA
          objB.b.c = 333
          console.log(objA);
          console.log(objB);
        };
        
      };
    }
  </script>
</body>

使用第三方库

最简单的和最稳妥的方法当然是使用第三方库去实现,比如函数库lodash的_.cloneDeep方法