思考
这个代码为什么具有深拷贝作用
obj = JSON.parse(JSON.stringify(deepCloneObj));
浅拷贝与深拷贝
在JavaScript中,对于Object和Array这类引用类型值,当从一个变量向另一个变量复制引用类型值时,这个值的副本其实是一个指针,两个变量指向同一个堆内存中的对象,改变其中一个变量,另一个也会受到影响。
这种拷贝分为两种情况:拷贝引用和拷贝实例,也就是我们说的浅拷贝和深拷贝
浅拷贝
将原对象/数组的引用直接赋给新对象/数组,新对象/数组只是原对象/数组的一个引用
深拷贝
创建一个新的对象/数组,将原对象的各项属性的“值”(数组的所有元素拷贝过来),是“值”而不是“引用”。
实例1:利用 parse() 方法和 stringify() 方法实现对一个对象的深拷贝。
stringify() 方法可以将一个JS对象序列化一个JSON字符串,parse()方法可以将JSON字符串反序列化为一个JS对象。通过这两个方法可以实现对象的深拷贝。
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj))
}
const obj = {
arr: ['a', 'n', 'd'],
number: 1,
obj: {
name: 'Aranl',
age: 18
},
func: function() {
return 'ok'
}
}
const objClone = deepClone(obj)
console.log(objClone) // =>
/*
[object Object] {
arr: ["a", "n", "d"],
number: 1,
obj: [object Object] {
age: 18,
name: "Aranl"
}
}
*/
在上面的例子中,我们可以看出,原对象的方法在拷贝的过程中丢失了,原因是在序列化JS对象的过程中,所有的函数和原型成员会被有意的忽略。
实例2:利用 parse() 方法和 stringify() 方法实现将一个或者或多个对象深拷贝到目标对象中
/**
* 对传入一个目标对象和一个对象或多个对象进行深拷贝操作,若属性相同,则取用后面对象的值
* 返回深拷贝操作后的对象
* @param { Object } target
* @param 一个或多个 { Object } sources
* @return { Object } target
*/
function deepCloneObjects(target, ...sources) {
sources.forEach(src=>{
for(let key in src) {
if(typeof src[key] === 'object') {
target[key] = JSON.parse(JSON.stringify(src[key]))
}else {
target[key] = src[key]
}
}
})
return target
}
实例3: 通过递归的方式,实现对一个对象进行深拷贝
/**
* 使用for in拷贝对象、数组 可拷贝方法
* @param {object} obj 拷贝模式
* @returns {array, object} 返回拷贝后的数组、对象
*/
function deepClone(obj) {
let temp = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
temp[key] = deepClone(obj[key])
} else {
temp[key] = obj[key]
}
}
}
return temp
}
let obj = [1,2,{m:1}]
let deepCloneObj = deepClone(obj)
deepCloneObj.push(3)
console.log(obj, deepCloneObj)
实例4: 通过递归的方式,实现对一个或者多个对象深拷贝
/**
* 对传入一个对象进行深拷贝操作,多个对象进行深拷贝“merge”操作
* 返回深拷贝“merge”操作后的对象
*
* @param 一个或多个 { Object } obj
* @return { Object } obj
*/
function mergeObj() {
const _copy = (obj1,obj2) => {
const _obj = Object.assign({}, obj1);
for (const key in obj2) {
const type1 = toString.call(_obj[key])
const type2 = toString.call(obj2[key])
if (type1 === type2 && type2 === '[object Array]') {
_obj[key] = _obj[key].concat(obj2[key])
} else if (type1 === type2 && type2 === '[object object]') {
_obj[key] = _copy(_obj[key], obj2[key])
} else {
_obj[key] = obj2[key]
}
}
return _obj
}
const _args = Object.assign({}, arguments)
let _len = Object.keys(_args).length
while (_len-- > 0) {
_args[_len - 1] = _copy(_args[_len - 1], _args[_len]);
}
return _args[0]
}