这篇是深摸面试题的第一期,该系列主要是博主在干完手头上的工作or页面写完而数据接口还没给or项目稳定没需求时摸鱼看面试题,把一些较为简单的面试题深入渗透并且综合整理而开的专栏。因为主要是给自己未来复习用的,就不会花太多功夫排版了哈~
1、首先说说浅拷贝与深拷贝的区别
作者:Mike丶
https://www.cnblogs.com/mikeCao/p/8710837.html
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。
浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。
2、常用的深拷贝方法
1.JSON.parse(JSON.stringify())
2.函数库lodash的_.cloneDeep方法
3.手写递归
4.jQuery.extend()方法
3、使用JSON.parse(JSON.stringify())实现深拷贝的缺点
作者:立志成为大神的阿俊
https://www.jianshu.com/p/6b2e4da4b22c
对比我们原先创建的对象和新对象,我们发现:
1.不会拷贝对象上的value值为undefined和函数的键值对
2.nan,无穷大,无穷小会转为null
自定义对象测试可见:
1.将constructor强行转为Object
2.无法获取自己原型链上的内容,只能获取Object原型内容
值得提的是:date对象经过序列化会变成date字符串
总结:由于拷贝函数本身没有什么用处(不能改变函数内部代码,顶多给函数加点私有属性),
若不在乎
1.取不到值为undefined的键
2.NaN和无穷转变为null
3.原型内容
4.date对象转为date字符串
则可以使用JSON序列化进行深拷贝
作者:别闹23
https://www.jianshu.com/p/52db1d0c1780
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
总结:
用法简单,然而使用这种方法会有一些隐藏的坑:因为在序列化JavaScript对象时,所有函数和原型成员会被有意忽略。
通俗点说,JSON.parse(JSON.stringfy(X)),其中X只能是Number, String, Boolean, Array, 扁平对象,即那些能够被 JSON 直接表示的数据结构。
4、较为完善的深拷贝方法
作者:立志成为大神的阿俊
https://www.jianshu.com/p/6b2e4da4b22c
思路:
1.判断是否为引用类型
2.判断是否为String对象、Number对象以及Bool对象
3.对日期对象做处理
4.区分数组和对象做处理
5.对函数做拷贝
6.还有一些内置类没做处理(如正则,错误等),后续有时间再处理
let deepCopy = function(data = {}){
//string,number,bool,null,undefined,symbol
//object,array,date
if(data&&typeof data==="object"){
//针对函数的拷贝
if(typeof data==="function"){
let tempFunc = data.bind(null)
tempFunc.prototype = deepCopy(data.prototype)
return tempFunc
}
switch(Object.prototype.toString.call(data)){
case "[object String]":return data.toString()
case "[object Number]":return Number(data.toString())
case "[object Boolean]":return new Boolean(data.toString())
case "[object Date]":return new Date(data.getTime())
case "[object Array]":
let arr = []
for(let i=0;i<data.length;i++){
arr[i] = deepCopy(data[i])
}
return arr
//js自带对象或用户自定义类实例
case "[object Object]":
let obj = {}
for(let key in data){
//会遍历原型链上的属性方法,可以用hasOwnProperty来控制 (obj.hasOwnProperty(prop)
obj[key] = deepCopy(data[key])
}
return obj
}
}else{
//string,number,bool,null,undefined,symbol
return data
}
}
测试:
let a = {
hobbits:["sing","dance",{
read:["hlm","shz"],
walk:{
type:"sports",
content:"walk"
},
run:{
type:"sports",
content:"run"
},
}],
b:{
age:10,
c:{
name:"c"
}
}
}
b = deepCopy(a)
console.log(b)
// {
// hobbits:["sing","dance",{
// read:["hlm","shz"],
// walk:{
// type:"sports",
// content:"walk"
// },
// run:{
// type:"sports",
// content:"run"
// },
// }],
// b:{
// age:10,
// c:{
// name:"c"
// }
// }
// }
console.log(a===b) //false
总结主要的点:
1.遍历到对象和数组时,创建对应的类型的空数据
2.像空数据内填充内容
3.返回这个数组或者对象
其余方案补全:
作者:小小小小小亮
https://blog.csdn.net/liwusen/article/details/78759373
5、js 时间对象(Date)深拷贝
上面其实多多少少都有提到了,不过事件对象面试时经常会抽出来单独问,所以就当再拓展一下吧
所有对象都有valueOf方法,valueOf方法对于:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueOf()方法只是简单返回这个对象本身。
对于Date类型:
因为valueOf方法,日期类定义的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒数.因此我们可以在Date的原型上定义克隆的方法:可以保证两个对象的值一致,但是内容地址不同。
Date.prototype.clone=function(){
return new Date(this.valueOf());
}
var date=new Date();
var newDate=date.clone();
希望该系列能保持更新吧,每天进步一点点,THX~