前言
作为面试必问的题目之一,看似简单的问题,有很多细节有时候还是很难注意到。这次对JS的深拷贝与浅拷贝做一个总结。
概念
浅拷贝:拷贝对象第一层基本数据类型数据,若有子对象,则拷贝子对象地址,操作子对象时互有影响**
深拷贝:完全拷贝对象,对两个对象进行操作时互不影响**
JS数据类型
-
基本数据类型(string,number,boolean,null,undifuned)
特性:原始值不可变,存放于栈中,当进行赋值操作时,生成的两个变量互不干扰
-
引用数据类型(对象,函数组等)
特性:值可变,存放于堆中,当进行赋值操作时,拷贝的是引用地址,两个地址指向同一对象,两个变量对这个对象均可操作(深浅拷贝均针对引用数据类型)
浅拷贝
-
定义
将对象的第一层基本数据类型值进行拷贝,当遇见子对象时拷贝其地址。当对拷贝对象的基本数据类型属性值操作时,两者互不影响,而对拷贝对象的子对象进行操作时,原对象的子对象也会发生变化
-
实现
遍历赋值
function shallowCopy(obj){
let des={}
for(let i in obj){
if(obj.hasOwnProperty(obj[i])){ //去除原型链属性
des[i]=obj[i]
}
}
return des
}
基本数据类型进行了值拷贝,而对象则拷贝了地址
Object.assign()
let des = Object.assign({},obj)
拷贝目标对象所有可枚举属性,但为浅拷贝
Array.property.slice()
let des = arr.slice()
对数组进行拷贝,但为浅拷贝
let des = arr.concat()
同上
深拷贝
-
定义
完全拷贝目标对象,无论怎么操作拷贝对象,目标对象均不变化。两者互不影响
-
实现
递归拷贝
function isObj(obj){
return (typeof obj === 'object' || typeof obj === 'function') && obj != null
}
function deepClone(obj){
let des=Array.isArray(obj)?[]:{}
for(let i in obj){
des[i]=isObj(obj[i])?deepClone(obj[i]):obj[i]
}
return des
}
let a = {
name:'hkj',
obj:{
age:30
},
b:undefined,
c:Symbol(11),
arr:[1,2,3],
time:new Date(),
reg:/\d/,
fun:()=>{return}
}
let b=deepClone(a)
console.log(a)
console.log(b)

- 提示:这种方式只对数组对象及基本数据类型有效,date、正则及函数,无法拷贝,均生成空对象
JSON.parse(JSON.stringify(obj))
let a = {
name:'hkj',
obj:{
age:30
},
b:undefined,
c:Symbol(11),
arr:[1,2,3],
time:new Date(),
reg:/\d/,
fun:()=>{return}
}
let b=JSON.parse(JSON.stringify(a))
console.log(a)
console.log(b)

- 提示:通常情况下推荐这种方式深拷贝,能解决大多数问题。要注意的是,symbol,undifined,正则,函数无法拷贝,时间会字符串化