在瞭解JS的淺拷貝與深拷貝以前,咱們須要先知道什麼是值傳遞與引用傳遞。程序員
在JS中,基本類型值的拷貝是按值傳遞的,而引用類型值的拷貝則是按引用傳遞的。經過值傳遞的變量間不會有任何牽連,互相獨立;可是引用傳遞拷貝的變量間則會相互影響,修改其中任何一方所引用的對象的值都會在另外一方中體現,之因此會有這樣的表現和JS的內存機制有關。數組
JS的內存也分爲堆和棧,可是注意這裏的堆棧和數據結構的堆棧是不一樣的概念。數據結構
棧:由系統自動分配,自動回收,效率高,但容量小函數
堆:由程序員手動分配內存,而且手動銷燬(高級語言如JS中有垃圾自動回收機制),效率不如棧,但容量大spa
JS定義的基本類型值會被存放在棧內存中,JS能夠直接訪問棧內存,因此訪問存放基本類型值的變量能夠直接訪問到基本類型值。而引用類型由於其大小不固定,系統會爲引用類型分配堆內存存放,而只將指向該堆內存空間的指針(引用)存放在棧中,JS不予許直接訪問存放引用類型值的堆內存,只能經過訪問引用間接的訪問引用類型值。這樣一來,咱們訪問引用類型值時,實質上只是在訪問它的引用,而後再按照這個引用地址去堆中找到它的實際內容。指針
所以進行拷貝的時候,對於基本類型值變量,系統會爲新變量單獨開闢一個新的棧內存空間,並將源變量保存的基本類型值拷貝一份保存到裏面。而對於引用類型值,新變量拷貝獲得的只是引用對象的引用而已,這麼一來,經過兩個變量保存的引用訪問到的實質就是同一塊堆內存,也就是同一個對象。code
let person = { name : "kelly", love : { sport : "swim", movie : "love story" } } let new_person = person new_person.name = "lin" console.log( person ) console.log( new_person )
淺拷貝是指在進行對象拷貝的時候,只對對象的第一層鍵值進行拷貝。對象
在上面的例子當中,若是不但願修改 new_person 對象的 name 值的時候,源對象的 name 值也跟着一塊兒改變,咱們能夠嘗試對拷貝過程作一些處理,而再也不只是簡單的直接賦值。blog
let person = { name : "kelly", love : { sport : "swim", movie : "love story" } } let new_person = { } function shallowCopy( target, source ){ if( !source || typeof( source ) !== "object" ){ return } if( !target || typeof( target ) !== "object" ){ return } for( let key in source ){ if( source.hasOwnProperty( key ) ){ target[ key ] = source[ key ] } } } shallowCopy( new_person, person ) new_person.name = "lin" console.log( new_person ) console.log( person )
這時候咱們修改了拷貝對象的 name 值,源對象的 name 值不會再跟着改變了,但是當咱們修改屬性 sport 的值的時候,源對象的 sport 值卻又跟着改變了。遞歸
shallowCopy( new_person, person ) new_person.love.sport = "run" console.log( new_person ) console.log( person )
Object.assign( new_person, person ) new_person.name = "lin" new_person.love.sport = "run" console.log( new_person ) console.log( person )
除此以外,Array 的 concat 方法和 slice 方法實現的也是數組對象的淺拷貝。
let arr = [ 1, [ 2, 3, 4 ] ] let arr_concat = arr.concat( ) let arr_slice = arr.slice( 0 ) arr_concat[ 1 ][ 0 ] = "NO" console.log( arr ) //[ 1, [ 'NO', 3, 4 ] ] console.log( arr_concat ) //[ 1, [ 'NO', 3, 4 ] ] arr_slice[ 1 ][ 1 ] = "NONO!" console.log( arr ) //[ 1, [ 'NO', 'NONO!', 4 ] ] console.log( arr_slice ) //[ 1, [ 'NO', 'NONO!', 4 ] ]
數組 arr,arr_concat, arr_slice 的第二個元素引用的都是同一個堆內存對象。
function deepCopy( target, source ){ if( !source || typeof( source ) !== "object" ){ return } if( !target || typeof( target ) !== "object" ){ return } for( let key in source ){ if( source.hasOwnProperty( key ) ){ if( source[ key ] && typeof( source[ key ] ) === "object" ){ target[ key ] = target.constructor === "Array" ? [ ] : { } deepCopy( target[ key ], source[ key ] ) } else { target[ key ] = source[ key ] } } } } deepCopy( new_person, person ) new_person.name = "lin" new_person.love.sport = "run" console.log( new_person ) console.log( person )
var obj = { a: 1, b: 2, c: undefined, sum: function() { return a + b } }; var obj2 = JSON.parse( JSON.stringify( obj ) ) console.log( obj2 )