JS中,一般的賦值傳遞的都是對象/數組的引用,並沒有真正的深拷貝一個對象,如何進行對象的深拷貝呢?
var a = {name : 'miay'}; var b = a; b.name = 'Jone'; console.log(a.name) //Jone
上述代碼中,b指向a所指向的棧對象,也就是說a,b指向同一個棧對象,這種屬於對象的淺拷貝。
var a = {name : 'miay'}; var b = Object.assign({},a); console.log(a === b) //false b.name = 'chris'; console.log(a.name) //miya
上述代碼將原對象拷貝到一個空對象中,a,b指向的是不同的棧對象,所以對b.name重新賦值不會影響到a.name,但是如果a.name是一個對象的引用,而不是一個字符串,那么a.nam和b.name指向的棧空間就是同一個了,看下面的栗子:
var a = {name:{firstName:"tang",lastName:"jiao"}} var b = Object.assign({},a) console.log(a === b); //false b.name.firstName = "chen" console.log(a.name.firstName) //chen
可以看出,Object.assign只是介於對象的深克隆和淺克隆之間的一種拷貝。具體來說也只是淺拷貝。對於對象屬性值為引用類型時,賦值時也是對於棧對象的引用罷了,那如何真正的進行對象的深拷貝呢?
使用JSON.parse()和JSON.stringify()對對象進行深拷貝
var clone = function(obj){ return JSON.parse(JSON.stringify(obj)); } var a = { a:function(){console.log('hello world')}, b:{c:1}, c:[1,2,3], d:'tang', e:new Date(), f:null, g:undefined } var b = clone(a); console.log(b)
可以看出,上述clone的方法會忽略function和undefined的字段,對date類型支持貌似也不友好。而且只能克隆原始對象自身的值,不能克隆它繼承的值,參考代碼如下:
function Person(name){ this.name = name; } var tang = new Person('miya'); var newtang = clone(tang) tang.constructor === Person //true newtang.constructor === Person //false console.log(newtang.constructor) //ƒ Object() { [native code] }
結論:對於純數據的json對象的深克隆,可以使用JSON.parse()和JSON.stringify()方法,自己可以寫個兼容function,undefined,繼承,Date的深拷貝的方法:
var clone = function(obj){ if(obj === null) return null; if(obj.constructor !== 'object') return obj; if(obj.constructor === Date) return new Date(obj); if(obj.constructor === RegExp) return new RegExp(obj); var newObj = new obj.constructor(); //保持繼承的原型 for(var key in obj){ if(obj.hasOwnProperty(key)){ var val = obj[key]; newObj[key] = typeof val === 'object' ? arguments.callee(val):val; } } return newObj; }
經過驗證,上述的原型的繼承,還是function,undefined,日期,正則等都完美實現深拷貝!
這里運用的就是建立一個新的對象,進行原始對象自有屬性的拷貝,遇到引用類型則繼續該方法的執行,非引用類型直接賦值。
我唯一知道的就是自己的無知。【完】
【我所知道的只有一件事,那就是我什么也不知道。】 ——蘇格拉底
[BGM] The Old Measure ——Daniel Martin Moore