一、面试官灵魂三连问:

  1. 你知道哪些拷贝的方法?
  2. 让你实现一个深拷贝怎么实现?
  3. 那像正则、Set、Map、函数等如何拷贝?

二、浅拷贝方法

自己创建一个新对象,来接收你要重新复制或引用的对象值。如果对象属性是基本数据类型,复制的就是基本数据类型的值给新对象;如果属性是引用数据类型,复制的就是内存中的地址,如果一个对象改变了这个属性值,那么会影响到另一个对象。

1.Object.assign()

  • 不会拷贝对象的继承属性
  • 不会拷贝对象的不可枚举属性
  • 可以拷贝symbol类型

扩展:什么是对象的不可枚举属性?

对象的每一个属性都有一个描述对象,用来描述和控制该对象的属性行为
使用Object.getOwnPropertyDescriptor() 方法来获取描述对象
通过Object.definedProperty() 来设置-----是不是很熟悉,这是vue中数据绑定的方法

例如

let obj = {
name : '123'
}
console.log(Object.getOwnPropertyDescriptor(obj,'name'))
/*
输出内容
configurable:true 能否通过delete删除此属性
enumerable : true 表示属性是可以枚举 即是否通过for in 或 Object.keys() 返回属性
value: '123'
writable: true 表示能否修改属性的值
*/ // 设置属性
Object.defineProperty(obj,'名字',{
value:'不可枚举属性',
enumerable:false
})

扩展:如何知道是“不可枚举属性”?

// 方法一:看颜色
console.log(obj)
/*
输出:
{
name: "123" // 控制台里是深色字体
名字: "不可枚举属性" // 控制台里是浅色字体
}
*/
// 方法二:使用以下四个方法
/*
四个操作会忽略enumerable为false
for in
Object.keys()
Object.assign()
JSON.stringify()
*/
// 我们实测下
for(let prop in obj){
console.log(prop)
}
/*
输出:name
*/
console.log(Object.keys(obj))
/*
输出: ['name']
*/
console.log(Object.assign({},obj))
/*
输出: {name: '123'}
*/
console.log(JSON.stringify(obj))
/*
输出: '{"name":"123"}'
*

2.扩展运算符方式

let obj2 = {...obj1}
let arr2 = [...arr1] //跟arr.slice()一样的效果

3.concat拷贝

let arr1 = [1,2,3]
let arr2 = arr1.concat()

4.slice拷贝数组

let arr1 = [1,2,3]
let arr2 = arr1.slice()

手动实现浅拷贝

var deepClone = target => {
//判断是否是对象类型,不是对象类型的话,直接返回本身
if ((typeof target === "object" || typeof target === 'function') && target !== null) {
//判断目标是数组还是对象
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
//只拷贝自身属性,不拷贝继承属性,所以使用hasOwnProperty(),当属性是继承属性则返回false
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop];
}
}
return cloneTarget;
} else {
return target;
}
};

三、深拷贝实现方法

1. JSON.stringify() 实现深拷贝

JSON.parse(JSON.stringify(target))

缺点:
1:拷贝的对象中如果存在undefined,function,symbol这几种类型,经过JSON.stringify()序列化后的字符串的这几个键会消失。
2: 拷贝Date引用类型会变成字符串
3: 无法拷贝不可枚举属性
4: 无法拷贝对象的原型链
5: 拷贝RexExp引用类型会变成空对象
6: 含有NaN,Infinity,-Infinity 经过JSON序列化后会变成null
7: 无法拷贝对象循环引用,记对象成环。

2.递归实现深拷贝

function deepCopy(target) {
if((typeof target !== 'object' || typeof target !== 'function') && target === null) return false
let res = Array.isArray(target) ? [] : {}
for(let k in target) {
// 如果目标数据上有属性(键)(key)
if(target.hasOwnProperty(k)) {
// 如果目标数据上属性的值,为object,就递归,不是object,就取到属性值,并放入我们新建的空数组/对象中
res[k] = typeof target[k] === 'object' ? deepCopy(target[k]) : target[k]
}
}
return res
}

测试下

// test用例1: null
var a = null // false
// test用例2: 不可枚举属性
var b = {name:'张三',like:['girl','cat']}
Object.defineProperty(b,'age',{
value: '18不可枚举属性',
enumerable: false
})
for(let prop in b){
console.log(prop)
}
/*
'name'
'like'
*/
// test用例3: 非数组、对象的引用类型
var c = new Date()
var d = deepCopy(c)
console.log(d)
/*
{}
*/
// test用例4:
const e = {}
e.e = e
deepClone(e)
/*
出现内存泄漏
*/
// test用例5: 对象、数组深层嵌套
var i = {rep:'apple',like:['women',{boy:{name:'王五',like:[69,'sex']}}]}
// 可以

通过测试可以看出

缺点:
1: 不可复制不可枚举属性以及symbol类型
2:只针对普通的引用类型做递归
3:没有解决对象成环

3.使用WeakMap解决对象成环

const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && typeof target !== null
function deepClone(target,map = new WeakMap()) {
//当weakmap 中已经存在这个对象,直接返回即可,不用在进行拷贝
if(map.get(target)) return target
if(!isObject(target)) return target
// 没有存在 就在weakmap中添加
map.set(target,true)
let res = Array.isArray(target) ? [] : {}
for(let k in target) {
if(target.hasOwnProperty(k)) {
res[k] = typeof target[k] === 'object' ? deepClone(target[k],map) : target[k]
}
}
return res
}

使用weakmap的原因是 weakmap并不会给对象增加引用次数,即对象可以被垃圾回收机制清除,不会占据内存,造成浪费性能。

四、对非一般引用类型的拷贝方法

由于其他类型较多,所以我们可以将这些类型分类后拷贝,首先看一下lodash 枚举出的一些类型。

按照类型分为可遍历类型和不可遍历类型。可遍历类型比如Set和Map等,不可遍历类型为RegExp和Date等。

扩展:获取数据类型

typeof:能准确判断基本数据类型,但一般复杂数据类型无法判断

instanceof:能准确判断复杂数据类型,但基本数据类型不行

Object.property.toString.call:全部可以

现在,假定我们封装好了isObject,isSet,isDate,等

1.拷贝Set

  function deepClone (val) {
if (isSet(val)) {
const set = new Set()
val.forEach(item => {
set.add(deepClone(item))
})
}
}

Map类型和Set类型类似,所以不再重复说明。

2.拷贝正则和Date

  function deepClone (val) {
const Ctor = val.constructor
if (isDate(val)) {
return new Ctor(+val)
} else if (isRegExp(val)) {
const reFlags = /\w*$/;
// 此处不用flags的原因在于flags方法返回的修饰符是按照字母顺序排列的
const reg = new Ctor(val.source, reFlags.exec(val))
reg.lastIndex = val.lastIndex
return reg
}
}

其他不可遍历类型类似,每个不可遍历类型有自己的特性,抓住数据类型的特性进行克隆就行。

3.拷贝函数

lodash对函数的处理为直接返回,这点根据函数的特性也能理解,克隆函数实际并无意义。如果必须实现的话需要考虑箭头函数和普通函数,箭头函数是没有原型的。克隆箭头函数比较简单,直接调用函数的toString方法,然后eval解析即可,普通函数需要正则匹配函数体,再通过new Function生成。

总结给大家推荐一个实用面试题库

 1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

2、前端技术导航大全      推荐:★★★★★

地址:前端技术导航大全

3、开发者颜色值转换工具   推荐:★★★★★

地址 :开发者颜色值转换工具

【面试题】面试官:请你实现一个深拷贝,那如果是正则/set/函数怎么拷贝?的更多相关文章

  1. Android相关面试题---面试官常问问题

    版权声明:本文为寻梦-finddreams原创文章,请关注: http://blog.csdn.net/finddreams/article/details/44513579 一般的面试流程是笔试完就 ...

  2. 阿里面试官让我实现一个线程安全并且可以设置过期时间的LRU缓存,我蒙了!

    目录 1. LRU 缓存介绍 2. ConcurrentLinkedQueue简单介绍 3. ReadWriteLock简单介绍 4.ScheduledExecutorService 简单介绍 5. ...

  3. 面试官问,说一个你在工作非常有价值的bug

    如果你去参考面试,做足了准备,面对面试官员从容不迫,吐沫横飞的大谈自己的工作经历.突然,面试官横插一句:说一个你在工作非常有价值的bug.顿时,整个空气都仿佛都凝固了!“What?”... 我想没几个 ...

  4. 阿里P8面试官:如何设计一个扛住千万级并发的架构?

    大家先思考一个问题,这也是在面试过程中经常遇到的问题. 如果你们公司现在的产品能够支持10W用户访问,你们老板突然和你说,融到钱了,会大量投放广告,预计在1个月后用户量会达到1000W,如果这个任务交 ...

  5. redis分布式锁,面试官请随便问,我都会

    目录 前言 实现要点 错误解锁方式 正确加锁释放锁方式 前言 现在的业务场景越来越复杂,使用的架构也就越来越复杂,分布式.高并发已经是业务要求的常态.像腾讯系的不少服务,还有CDN优化.异地多备份等处 ...

  6. 【Java】面试官灵魂拷问:if语句执行完else语句真的不会再执行吗?

    写在前面 最近跳槽找工作的朋友确实不少,遇到的面试题也是千奇百怪,这不,一名读者朋友面试时,被面试官问到了一个直击灵魂的问题:if 语句执行完else语句真的不会再执行吗?这个奇葩的问题把这名读者问倒 ...

  7. [Interview]读懂面试问题,在面试官面前变被动为主动

    面试是供需双方心理的较量,作为求职者来说,了解对方问题的内涵,做到“明明白白他的心”,就能变被动为主动.因此,读懂面试问题,掌握面试考官的提问的目的,有准备.有针对性地回答,对提高应聘的成功率是有很大 ...

  8. 攻略前端面试官(三):JS的原型和原型链

    本文在个人主页同步更新~ 背就完事了 介绍:一些知识点相关的面试题和答案 使用姿势:看答案前先尝试回答,看完后把答案收起来检验成果~ 面试官:什么是构造函数 答:构造函数的本质是一个普通函数,他的特点 ...

  9. 当阿里面试官问我:Java创建线程有几种方式?我就知道问题没那么简单

    这是最新的大厂面试系列,还原真实场景,提炼出知识点分享给大家. 点赞再看,养成习惯~ 微信搜索[武哥聊编程],关注这个 Java 菜鸟. 昨天有个小伙伴去阿里面试实习生岗位,面试官问他了一个老生常谈的 ...

  10. 【Nginx】面试官竟然问我Nginx如何生成缩略图,还好我看了这篇文章!!

    写在前面 今天想写一篇使用Nginx如何生成缩略图的文章,想了半天题目也没想好,这个题目还是一名读者帮我起的.起因就是这位读者最近出去面试,面试官正好问了一个Nginx如何生成缩略图的问题.还别说,就 ...

随机推荐

  1. 道长的算法笔记:KMP算法及其各种变体

    (一)如何优化暴力算法 Waiting... (二)KMP模板 KMP 算法的精髓在于 \(next\) 数组,\(next[i]=j\) 代表 \(p[1,j]=p[i-j+1,i]\),\(nex ...

  2. 2022CSP-J线上游记

    写在前面 安徽CSP取消了-- 去年CSP考炸的我本来想今年一雪前耻(bushi),结果-- T1 第一题大毒瘤! 首先观察数据可以分类如下两种情况: \(a = 1\) 直接输出\(1\),retu ...

  3. 已完成 10000 多次提交,Solon Java Framework v1.12.1 发布

    一个更现代感的 Java 应用开发框架:更快.更小.更自由.没有 Spring,没有 Servlet,没有 JavaEE:独立的轻量生态.主框架仅 0.1 MB. @Controller public ...

  4. Java基础篇——集合框架

    集合--对象的容器 集合与数组相似,不同的是,集合的长度可变并且只能组合引用类型数据,如果要组合基本类型,则需要装箱成包装类 Collection体系集合 Collection父接口 Collecti ...

  5. 数据库实践丨使用MTK迁移Mysql源库后主键自增列导致数据无法插入问题

    摘要:用户使用Mogdb 2.0.1版本进行业务上线测试,发现在插入数据时,应用日志中提示primary key冲突,用户自查业务SQL没有问题,接到通知后,招手处理故障. 本文分享自华为云社区< ...

  6. MySQL 插入数据 数据重复 从另一个表导入数据

    当使用MySQL插入数据时,我们可以根据需求选择合适的插入语句. 一.方法分类 二.具体方法 使用场景 作用 语句 注意 常规插入 忽略字段名 insert into 表名 values (值1, 值 ...

  7. 超详细手把手教你cordova开发使用指南+自定义插件,jsbridge

    Cordova是什么 使用前端技术 开发跨平台web App的工具 底层原理:HTML+CSS搭建页面, JS和原生交互 交互原理:Cordova插件 环境配置 安卓开发基础环境搭建的文章可以参考一下 ...

  8. JavaScript 文件上传

    一.普通文件上传 JavaScript 可以使用表单提交来实现文件上传.首先,在 HTML 中创建一个文件输入框: <input type="file" id="f ...

  9. MyBatis的使用七(处理表与表之间的关系)

    本文主要讲述mybatis的处理表与表之间的关系 一. 介绍t_emp和t_dept表 1. t_emp表结构 2. t_dept表结构 二. 数据表的关系 1. 阐明关系 一个部门可以有多个员工,但 ...

  10. 遗传算法求TSP问题

    一.实验内容及目的 本实验以遗传算法为研究对象,分析了遗传算法的选择.交叉.变异过程,采用遗传算法设计并实现了商旅问题求解,解决了商旅问题求解最合适的路径,达到用遗传算法迭代求解的目的.选择.交叉.变 ...