js對象的深淺拷貝
JS數據類型可以分為(ES5,暫時不考慮ES6):
簡單數據類型:Number、String、undefined、boolean
復雜數據類型:Object、Array
簡單的數據類型,往往是賦值操作,而復雜數據類型是引用操作。
賦值操作我們就不講了,主要看看引用操作把
var arr = [1,2,3]; var arr2 = arr; arr2.push(4); console.log(arr);//輸出[1,2,3,4]
明明是對arr2進行的操作,為什麽arr也變化了呢?因為js存儲對象都是存地址的,所以arr2實際存儲的是:"arr在內存中的地址";
由此我們可以明白,既然都是指向同一個內存地址,當我們改變任何一個的時候,都會改變。
知道這個基本的特性那麽我們可以試圖來理解下淺復制和深復制
首先看一段代碼,我們再來講解下概念
var obj ={a:1,b:2,c:[1,2]}; var shallowCopy = shallow(obj); function shallow(obj){ var shallowObj = {}; for(var name in obj){ if(obj.hasOwnProperty(name)){ shallowObj[name] = obj[name] } } return shallowObj } console.log(shallowCopy);//輸出的就是這個對象,我們實現了簡單的淺復制;
淺復制:只會將對象的各個屬性進行依次復制,並不會進行遞歸復制,而js存儲對象都是存地址的,所以淺復制會導致obj.c和shallowCopy.c 指向同一塊內存地址;會導致引用。
深復制:它不僅將原對象的各個屬性逐個復制出去,而且將原對象各個屬性所包含的對象也依次采用深復制的方法遞歸復制到新對象上。這就不會存在上面obj和shallowCopy的c屬性指向同一個對象的問題。(待會貼出深復制的代碼,這是個復雜的問題)
總結:需要註意的是,如果對象比較大,層級也比較多,深復制會帶來性能上的問題。在遇到需要采用深復制的場景時,可以考慮有沒有其他替代方案,在實際的引用場景中,也是淺復制更為常用。
我現在給出一個簡單版本的深復制,可以復制對象和數組(不考慮循環引用問題,函數對象問題),如果想深入研究的話可以看下這篇文章:http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/
在這裏我們只理解思想和簡單的實現,實際項目中可以考慮使用lodash(http://lodashjs.com/docs/),或者jQuery的extend的方法也同樣可以
function deepClone(obj){ var newObj = obj.constructor === Array ? []:{}; if(typeof obj !== ‘object‘){ return }else{ for(var i in obj){ if(obj.hasOwnProperty(i)){ newObj[i] = typeof obj[i] === ‘object‘?deepClone(obj[i]):obj[i]; } } } return newObj }
最核心的思想還是采用遞歸的方式,不斷進行,直到基本數據類型後,再復制
方法還有很多種,譬如還有使用JSON.stringify進行序列化,JSON.parse進行反序列化,實現"偷懶版"的深復制...等等
js對象的深淺拷貝