在面试中你是否遇到过如下场景:

Q:小朋友,你是否了解如何拷贝一个对象?

R:此时,机智的你可能会想到

Object.assign({}, obj);

Q:那如何深拷贝一个对象呢?

R:机智的你

JSON.parse(JSON.stringify(obj));

Q:使用stringify这种方式有何弊端?

  1. 性能问题,stringify再解析其实需要耗费较多时间,特别是数据量大的时候。

  2. 一些类型无法拷贝,例如函数(不输出),正则(输出空对象),时间对象(输出时间字符串),Undefiend(不输出)

  3. 遇到循环引用的对象会出错

  4. 同层(非同层)同引用的问题,理论下两个key对应的val如果指向同一个对象,拷贝也应该指向一个相同新地址才对

Q:那你能自己实现个深拷贝函数?

R:如下:

const deepClone = (obj) => {
// 非引用类型及函数将直接返回
if (!obj || typeof obj !== 'object') return obj; // 特殊的引用类型处理
switch(Object.prototype.toString.call(obj).slice(8, -1)) {
case 'Date':
return new Date(obj);
break;
case 'RegExp':
return new RegExp(obj);
break;
case 'String':
return new String(obj);
break;
case 'Number':
return new Number(obj);
break;
case 'Boolean':
return new Boolean(obj);
break;
} const result = obj instanceof Array ? [] : {}; for (let propName in obj) {
if (obj.hasOwnProperty(propName)) {
result[propName] = deepClone(obj[propName]);
}
} return result;
}

优点:实现了大多数数据类型的拷贝,所有非引用类型及引用类型的String Number Boolean Function Array Date RegExp

缺点:未考虑一些特殊的引用类型如Error Math Symbol Map Set JSON,函数属于引用拷贝,未解决循环引用的问题

Q:如何解决循环引用?

R:将父层级的数据缓存对比(可以顺带解决同层(非同层)同引用的问题)

const deepClone = (obj) => {
// 非引用类型及函数将直接返回
if (!obj || typeof obj !== 'object') return obj; // 特殊的引用类型处理
switch (Object.prototype.toString.call(obj).slice(8, -1)) {
case 'Date':
return new Date(obj);
break;
case 'RegExp':
return new RegExp(obj);
break;
case 'String':
return new String(obj);
break;
case 'Number':
return new Number(obj);
break;
case 'Boolean':
return new Boolean(obj);
break;
} const map = deepClone.map = deepClone.map || new Map(); // 使用map结构可以不必循环缓存,提高效率
if (map.get(obj)) {
return map.get(obj);
} const result = obj instanceof Array ? [] : {}; // 如果仔细观察可以发现解决了同层同引用的问题
map.set(obj, result); for (let propName in obj) {
if (obj.hasOwnProperty(propName)) {
result[propName] = deepClone(obj[propName]);
}
} return result;
}

Q:为什么函数还是指向原来的函数,而不创建新函数?

R:理论下函数也可以通过new Function(code)来创建新的函数,但是如果遇到闭包函数,我们无法得到原函数的外层定义的变量及其原有作用域链,这些在JS词法解析时完成的步骤我们无法得知,所有只能引用原函数比较好。

Sum: 上面实现的缺点主要是没有完全覆盖特殊引用类型,但其实我们平时应该不会遇到那些类型,所以可以凑合使用。如果还有其它的问题没有考虑到或者有出错的,希望大家可以帮忙指出。

参考


欢迎到前端学习打卡群一起学习~516913974

面试之JS深拷贝的实现的更多相关文章

  1. 2019前端面试系列——JS面试题

    判断 js 类型的方式 1. typeof 可以判断出'string','number','boolean','undefined','symbol' 但判断 typeof(null) 时值为 'ob ...

  2. 2019前端面试系列——JS高频手写代码题

    实现 new 方法 /* * 1.创建一个空对象 * 2.链接到原型 * 3.绑定this值 * 4.返回新对象 */ // 第一种实现 function createNew() { let obj ...

  3. js 深拷贝和浅拷贝

    js 深拷贝和浅拷贝 先举一下项目中遇到的两个例子: 例子1: var json = $.parseJSON(data.data);//data.data是接口返回的值var a = json.cha ...

  4. 老生常谈之js深拷贝与浅拷贝

    前言 经常会在一些网站或博客看到"深克隆","浅克隆"这两个名词,其实这个很好理解,今天我们就在这里分析一下js深拷贝和浅拷贝. 浅拷贝 我们先以一个例子来说明 ...

  5. 关于JS深拷贝和浅拷贝

    最近在前端开发中遇到一些问题,就是数组中的某个对象或某个对象的值改变之后,在不刷新页面的时候需要重新渲染值时,页面显示的还是原来的数据.比如: data{ A:[{id:1,num:1},{id:2, ...

  6. js深拷贝你还不会吗

    js深拷贝 在讲正题之前我们要先了解数据存储的方式 数据存储方式 在讲之前我们要先知道值类型和引用类型的存储方式. 在JavaScript数据类型中有两种数据类型. 值类型:字符串(String).数 ...

  7. 面试遇到的坑JS深拷贝和浅拷贝

    首先要搞明白深拷贝和钱拷贝的区别要先搞明白 栈和堆的区别 一.栈 栈存储基础数据类型,如: String.Number.Boolean.Null.Underined,这些简单的基础数据类型能够直接存储 ...

  8. 深入 js 深拷贝对象

    前言 对象是 JS 中基本类型之一,而且和原型链.数组等知识息息相关.不管是面试中,还是实际开发中我们都会碰见深拷贝对象的问题. 顾名思义,深拷贝就是完完整整的将一个对象从内存中拷贝一份出来.所以无论 ...

  9. [转] js深拷贝和浅拷贝

    一.数组的深浅拷贝 在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致 ...

随机推荐

  1. node实现防盗链

    什么是防盗链 盗链,顾名思义就是盗窃链接,就是在他人未经允许的情况下,去使用他人站点的任何图片,视频,内容等资源,来借此提升已用的站点的活跃度,严重损坏了别人的利益.那么防盗链就是采用一些办法阻止自己 ...

  2. nghttp2 交叉编译

    touch run.sh chmod 755 run.sh mkdir build cd build ../run.sh run.sh #!/bin/bash #cd build ../configu ...

  3. CKEDITOR (FCKEDITOR) --- 目前最优秀的可见即可得网页编辑器之一

    FCKEDITOR 编辑 同义词 CKEditor一般指FCKEDITOR FCKeditor是目前最优秀的可见即可得网页编辑器之一,它采用JavaScript编写.具备功能强大.配置容易.跨浏览器. ...

  4. Codeup 25594 Problem H 例题5-8 Fibonacci数列

    题目描述 输入一个正整数n,求Fibonacci数列的第n个数.Fibonacci数列的特点:第1,2个数为1,1.从第3个数开始,概述是前面两个数之和.即: 1,1,2,3,5,8,13,21 - ...

  5. L26 使用卷积及循环神经网络进行文本分类

    文本情感分类 文本分类是自然语言处理的一个常见任务,它把一段不定长的文本序列变换为文本的类别.本节关注它的一个子问题:使用文本情感分类来分析文本作者的情绪.这个问题也叫情感分析,并有着广泛的应用. 同 ...

  6. python操作数据库-SQLSERVER-pyodbc

    刚开始学python时,大家都习惯用pymssql去读写SQLSERVER.但是实际使用过程中,pymssql的读写性能以及可靠性的确不如pyodbc来的好. 正如微软官方推荐使用pyodbc库,作为 ...

  7. SpringBoot系列(九)单,多文件上传的正确姿势

    SpringBoot系列(九)分分钟解决文件上传 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配 ...

  8. jmeter 聚合报告参数解释

    label:每个请求的名称 样本:发送给服务器的请求数量 平均值:平均响应时间,默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以T ...

  9. [linux][MongoDB] mongodb学习(二):命令使用数据库

    使用数据库 # 查看数据库 > show dbs admin 0.000GB local 0.000GB # 查看表(集合) > show tables # 删除集合 > db.us ...

  10. Laravel - 上手实现 - 文件上传、保存到 public 目录下

    1.为了访问方便,将上传的文件保存在 public 目录下,需要进行修改配置. 找到 config/filesystems.php 文件然后修改 root.具体如下: 'local' => [ ...