IMAGINE'S BLOG IMAGINE'S BLOG
首页
  • 原生JS

    • JavaScript
  • 前端框架扩展

    • Vue
    • React
    • UI组件库
  • HTML
  • CSS
  • 浏览器
  • 分类
  • 标签
  • 归档
  • 技术文档
  • GitHub相关
  • Nodejs
关于
  • 网站
  • 友情链接
GitHub (opens new window)

peng

平平无奇的web前端开发一枚
首页
  • 原生JS

    • JavaScript
  • 前端框架扩展

    • Vue
    • React
    • UI组件库
  • HTML
  • CSS
  • 浏览器
  • 分类
  • 标签
  • 归档
  • 技术文档
  • GitHub相关
  • Nodejs
关于
  • 网站
  • 友情链接
GitHub (opens new window)
  • JavaScript

    • javascript 之 this 指向问题
    • JS循环遍历方法
    • 前端常用公共方法工具类
    • JS声明提升
    • JS合并数组对象中key值相同的数据
    • 数组对象求和
    • 前端路由实现
    • Promise(一):Promise 认识
    • Promise(二):手动实现一个 Promise
    • Promise(三):拓展方法实现
    • JS forEach踩坑
    • ES6-ES12 特性
    • async/await
    • JavaScript 数组去重
    • js对象排序
    • js if/else 语句优化策略
    • js深拷贝
      • JSON.stringify && JSON.parse
      • 递归实现深拷贝
        • 解决方法和思路:
    • 正则表达式校验
    • js笛卡尔积
    • Axios 封装
  • 框架扩展

  • 前端乱炖
  • JavaScript
peng
2022-09-05
目录

js深拷贝

# JSON.stringify && JSON.parse

这是最简单的 js 实现深拷贝方式了,原理是先将对象转换为字符串,再通过 JSON.parse 重新建立一个对象。

但这种方式存在一定的局限性:

不能复制 Function、正则、Symbol 循环引用会报错 相同引用会被重复复制 根据以上三点我们来验证,实测代码如下:

let obj = {
  func: function () {},
  reg: /^abc$/,
  syb: Symbol("demo"),
  str: "abc",
};

let copyObj = JSON.parse(JSON.stringify(obj));
console.log(copyObj);
1
2
3
4
5
6
7
8
9

打印结果:Function、正则和 Symbol 都没有被复制正确的值

如果 JSON.stringify 中传入一个循环引用的对象,就会报错:

我们来看看下面这段代码:

let obj1 = { name: "Mike" };
let obj2 = { age: 18 };

obj1.text1 = obj2; // {age:18}
obj1.text2 = obj2; // {age:18}

let copys = JSON.parse(JSON.stringify(obj1));

obj1.text1.age = 22;
copys.text1.age = 22;

console.log("原对象", obj1);
console.log("复制对象", copys);
1
2
3
4
5
6
7
8
9
10
11
12
13

结果: 我们看到当原对象的 text1.age 改变时 text2.age 也会改变;因为它们指向相同的对象。

但是,在复制对象中 text1 和 text2 分别指向两个对象,复制对象没有保持和原对象一样的结构;所以可以得出:

JSON 实现深拷贝不能处理指向相同引用的情况,相同的引用会被重复复制。


# 递归实现深拷贝

通过递归遍历实现深拷贝,对于简单类型,进行直接复制;引用类型,递归复制它的每一个属性。

function isObject(obj) {
  return obj !== null && typeof obj === "object";
}

function _cloneDeep(target) {
  if (!isObject(target)) return target;

  let result = Array.isArray(target) ? [] : {};

  const keys = Object.keys(target);
  for (let i = 0, len = keys.length; i < len; i++) {
    result[keys[i]] = cloneDeep(target[keys[i]]);
  }
  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

以上代码并没有考虑到循环引用和相同引用的问题;对于循环引用的对象使用的话会直接栈溢出,会出现和 JSON 方法一样的问题。

# 解决方法和思路:

1、通过闭包维护一个变量,变量中存储已经遍历过的对象

2、每次递归时判断当前的参数是否已经存在于变量中;如果已经存在,说明已经递归过该对象,就停止这次递归并返回上次递归该对象时的返回值

代码实现如下:

function isObject(obj) {
  // 判断类型
  return obj !== null && typeof obj === "object";
}

function _cloneDeep(obj) {
  let visitedObjs = [];
  function baseClone(target) {
    if (!isObject(target)) return target;

    for (let i = 0; i < visitedObjs.length; i++) {
      if (visitedObjs[i].target === target) {
        return visitedObjs[i].result;
      }
    }

    let result = Array.isArray(target) ? [] : {};

    visitedObjs.push({ target, result });

    const keys = Object.keys(target);
    for (let i = 0, len = keys.length; i < len; i++) {
      result[keys[i]] = baseClone(target[keys[i]]);
    }
    return result;
  }
  return baseClone(obj);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#JavaScript
上次更新: 2022/09/06, 10:30:04
js if/else 语句优化策略
正则表达式校验

← js if/else 语句优化策略 正则表达式校验→

最近更新
01
Axios 封装
09-06
02
MySQL数据库常用操作
09-06
03
解决element表格数据量过大导致页面渲染缓慢、卡顿问题
09-06
更多文章>
Theme by Vdoing | Copyright © 2020-2024 peng | IMAGINE
image | imgloc.com
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式