拷贝对象,需要做到2点:
1、新对象与旧对象具有相同的属性
2、新对象与旧对象具有相同的原型
1、对象浅拷贝 copy方法判断要copy的对象是不是数组,如果是就创建一个空数组,否则就创建对象
var obj={
name:'唐三',
age:'22',
phone:'1372476xxxx',
girlFreind:{
name:'小兔子'
}
}
Object.setPrototypeOf(obj,{address:'天斗帝国'})
function copy(obj){
var newObj =Array.isArray(obj)?[]:{}
for (let key in obj) {
newObj[key]=obj[key]
}
return newObj
}
var newObj=copy(obj);
问题1:obj和newObj是共享girlFreind这个对象
问题2:obj原型上的address也被copy到newObj对象里
解决问题2,再for in 使用hasOwnProperty方法,判断每一个属性是不是自身的属性:
function copy(obj) {
var newObj =Array.isArray(obj)?[]:{}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key]
}
}
return newObj
}
问题1,由于newObj只是拷贝了girlFreind这个对象的引用地址,导致无论谁修改这对象,都会互相同步。解决方案,判断每一个属性的值是不是对象或者数组,如果是就重新调用copy保存返回来的对象,否则就直接赋值。
function copy(obj) {
var newObj =Array.isArray(obj)?[]:{}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if(typeof(obj[key])==='object'||Array.isArray(obj[key])){
newObj[key]=copy(obj[key])
}else{
newObj[key] = obj[key]
}
}
}
return newObj
}
function deepClone(origin,hashMap=new WeakMap()) {
if (origin == undefined || typeof origin !== 'object') {
return origin;
}
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
console.log(hashMap);
const hashValue=hashMap.get(origin)
if(hashValue){
return hashValue;
}
const target = new origin.constructor();
hashMap.set(origin,target)
for (let key in origin) {
if (origin.hasOwnProperty(key)) {
target[key] = deepClone(origin[key],hashMap);
}
}
return target;
}
当前的对象自身属性现在已经是深拷贝了,但是访问newObj.address会的到一个undefined。由于newObj并没有拷贝都obj原型上的属性,所以接下来把obj的原型设置为newObj的原型。 Object.create(Object.getPrototypeOf(obj)):
function copy(obj) {
var newObj =Object.create(Object.getPrototypeOf(obj))
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if(typeof(obj[key])==='object'||Array.isArray(obj[key])){
newObj[key]=copy(obj[key])
}else{
newObj[key] = obj[key]
}
}
}
return newObj
}
又有一个问题,如果修改了obj的原型上的属性,newObj也会改变,因为他们指向同一个对象:
// obj.__proto__.address='星斗森林'
Object.getPrototypeOf(obj).address='星斗森林'
console.log(newObj.address)//星斗森林;
这个时候需要对对象的原型进行深度拷贝: 1、通过Object.getPrototypeOf获取对象的原型赋值给变量proto;
2、如果proto不为null且proto不是数组,就进行原型深度拷贝重新赋值给proto;
3、通过Object.setPrototypeOf设置newObj的原型为newObjPrototype,代码如下:第一种深度克隆
function copy(obj) {
var newObj = Array.isArray(obj) ? [] : {}
var proto = Object.getPrototypeOf(obj)
if (proto != null && !Array.isArray(proto)) {
proto= copy(proto)
Object.setPrototypeOf(newObj, proto)
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof(obj[key]) === 'object' || Array.isArray(obj[key])) {
newObj[key] = copy(obj[key])
} else {
newObj[key] = obj[key]
}
}
}
return newObj
}
第二种深度克隆:
function deepClone(origin) {
var proto = Object.getPrototypeOf(origin)
if (proto != null && !Array.isArray(proto)) {
proto=deepClone(proto)
}
var properties = Object.getOwnPropertyDescriptors(origin)
for (let key in properties) {
if (typeof(properties[key].value) === 'object') {
properties[key].value = deepClone(properties[key].value)
}
}
var obj = Object.create(proto, properties)
return obj
}
总结:
1、如果需要深度克隆对象,不考虑方法和继承,选择JSON.parse(JSON.stringify(obj))
2、如果考虑继承选择方法2。方法2保存了对象属性的完整信息,比如属性能不能枚举,第一种只是简单newObj[key] = obj[key]赋值,属性的描述信息都是默认值。