浅拷贝
拷贝基本数据类型时,不受任何影响,当拷贝引用类型时,只复制某个对象的引用,源对象也会被修改。
Object.assign
let obj = {
name:'jack',
age:20,
child:{
name:'mike',
age:1,
}
}
let target = {}
Object.assign(target,obj)
target.name = 'alice'
console.log(target.name); // alice
console.log(obj.name); // jack
target.child.name = 'haha'
console.log(target.child.name); // haha
console.log(obj.child.name); // haha
扩展运算符
let obj = {
name:'jack',
age:20,
child:{
name:'mike',
age:1,
}
}
let target = {...obj}
target.name = 'alice'
console.log(target.name); // alice
console.log(obj.name); // jack
target.child.name = 'haha'
console.log(target.child.name); // haha
console.log(obj.child.name); // haha
Array.prototype.slice()
slice()方法可以用来截取数组
let arr = [1,2,{name:'jack'}]
let newArr = arr.slice()
newArr[0] = 99
console.log(newArr[0]); // 99
console.log(arr[0]); // 1
newArr[2].name = 'mike'
console.log(newArr[2].name); // mike
console.log(arr[2].name); // mike
Array.prototype.concat()
concat() 方法用于合并两个或多个数组
let arr = [1,2,{name:'jack'}]
let newArr = arr.concat([3])
newArr[0] = 99
console.log(newArr[0]); // 99
console.log(arr[0]); // 1
newArr[2].name = 'mike'
console.log(newArr[2].name); // mike
console.log(arr[2].name); // mike
深拷贝
深拷贝会复制一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对象。
JSON.stringify 和 JSON.parse
function Person(){}
let obj = {
name:'jack',
age:20,
date:new Date(),
reg:new RegExp('\\w+'),
sayName:function(){
console.log(this.name);
},
number:NaN,
sex:undefined,
child:{
name:'mike',
age:1,
},
p: new Person()
}
let target = JSON.parse(JSON.stringify(obj))
console.log(target);
target.name = 'alice'
console.log(target.name); // alice
console.log(obj.name); // jack
target.child.name = 'haha'
console.log(target.child.name); // haha
console.log(obj.child.name); // mike
缺点:
- 拷贝的对象中如果有函数,undefined,symbol,当使用过
JSON.stringify()进行处理之后,都会消失。 - 无法拷贝不可枚举的属性;
- 无法拷贝对象的原型链;
- 拷贝 Date 引用类型会变成字符串;
- 拷贝 RegExp 引用类型会变成空对象;
- 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null
lodash
import cloneDeep from 'lodash/cloneDeep'
let obj = {
name: 'jack',
age: 20,
child: {
name: 'mike',
age: 1,
},
}
var newObj = cloneDeep(obj)
console.log(obj.child === newObj.child) // false
手写实现
function deepClone(obj, hash = new WeakMap()) {
// 如果不是对象或者null的直接返回
if (obj === null || typeof obj != 'object') {
return obj
}
// 日期对象直接返回一个新的日期对象
if (obj instanceof Date) {
return new Date(obj)
}
//正则对象直接返回一个新的正则对象
if (obj instanceof RegExp) {
return new RegExp(obj)
}
//如果循环引用,就用 weakMap 来解决
if (hash.has(obj)) {
return hash.get(obj)
}
// 获取对象所有属性的描述符
let allDesc = Object.getOwnPropertyDescriptors(obj)
// 创建新的对象,设置对象原型,对象属性和属性描述符
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
hash.set(obj, cloneObj)
// 遍历对象属性进行属性值的复制
for (let key of Reflect.ownKeys(obj)) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
cloneObj[key] = deepClone(obj[key], hash)
} else {
cloneObj[key] = obj[key]
}
}
return cloneObj
}
function Person() {}
let obj = {
name: 'jack',
age: 20,
boolean: true,
und: undefined,
nul: null,
arr: [1, 2],
[Symbol('1')]: 1,
date: new Date(),
reg: new RegExp('\\w+'),
sayName: function () {
console.log(this.name)
},
number: NaN,
child: {
name: 'mike',
age: 1,
},
p: new Person(),
}
Object.defineProperty(obj, 'innumerable', {
enumerable: false,
value: '不可枚举属性',
})
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 将loop设置成循环引用的属性
let cloneObj = deepClone(obj)
console.log(obj)
console.log(cloneObj)