JS的深淺拷貝

淺拷貝

前面已經(jīng)提到疙描,在定義一個對象或數(shù)組時,變量存放的往往只是一個地址业舍。當(dāng)我們使用對象拷貝時,如果屬性是對象或數(shù)組時升酣,這時候我們傳遞的也只是一個地址舷暮。因此子對象在訪問該屬性時,會根據(jù)地址回溯到父對象指向的堆內(nèi)存中噩茄,即父子對象發(fā)生了關(guān)聯(lián)下面,兩者的屬性值會指向同一內(nèi)存空間。

/**
 * 淺拷貝copy绩聘,只深拷貝了第一級的數(shù)值
 */
var a = { key: "111" }
function copy(p) {
  var c = {};
  for (var i in p) {
    c[i] = p[i];
  }
  return c;
}
a.key1 = 1
a.key2 = ["xiao", "xiao"] //引用類型是淺拷貝沥割,結(jié)果相同
var b = copy(a)
b.key = "22" //數(shù)值部分是深拷貝耗啦,結(jié)果不同
b.key3 = "333";
b.key2.push("xiao")
console.log(a)
console.log(b)

輸出結(jié)果:

{ key: '111', key1: 1, key2: [ 'xiao', 'xiao', 'xiao' ] }
{ key: '22',
  key1: 1,
  key2: [ 'xiao', 'xiao', 'xiao' ],
  key3: '333' }

上述語句輸入結(jié)果的原因是:
key1的值屬于基本類型,所以拷貝的時候傳遞的就是該數(shù)據(jù)段机杜;
但是key2的值是堆內(nèi)存中的對象帜讲,所以key2在拷貝的時候傳遞的是指向key2對象的地址,無論復(fù)制多少個key2椒拗,其值始終是指向父對象的key2對象的內(nèi)存空間似将。
用下圖來表示內(nèi)存中語句的賦值過程如下:

image.png

常見的淺拷貝的方法:(這里的淺拷貝指的是“只深拷貝第一級屬性”)

/**
 * ES6實現(xiàn)只深拷貝第一級的方法
 */
//1: Object.assign()
var a = { name: "暖風(fēng)" }
var b = Object.assign({}, a);
b.age = 18;
console.log(a.age);//undefined
console.log("-------------")

//2: 數(shù)組
var a = [1, 2, 3];
var b = a.slice();
b.push(4);
console.log(a)//[ 1, 2, 3 ]
console.log(b)//[ 1, 2, 3, 4 ]
var a = [{ "k": [1, 2] }]
var b = a.slice();
b[0].k = [1, 2]
console.log(a)//[ { k: [ 1, 2 ] } ]
console.log(b)//[ { k: [ 1, 2 ] } ]
console.log("-------------")

//3: concat
var a = [1, 2, 3];
var b = a.concat();
b.push(4);
console.log(a)//[ 1, 2, 3 ]
console.log(b)//[ 1, 2, 3, 4 ]
var a = [{ "k": [1, 2] }]
var b = a.slice();
b[0].k = [1, 2]
console.log(a)//[ { k: [ 1, 2 ] } ]
console.log(b)//[ { k: [ 1, 2 ] } ]
console.log("-------------")

//4 :...
var a = [1, 2, 3];
var b = [...a];
b.push(4)
console.log(a)//[ 1, 2, 3 ]
console.log(b)//[ 1, 2, 3, 4 ]
var a = [{ "k": [1, 2] }];
var b = [...a];
console.log(a)//[ { k: [ 1, 2 ] } ]
console.log(b)//[ { k: [ 1, 2 ] } ]

深拷貝

或許以上并不是我們在實際編碼中想要的結(jié)果,我們不希望父子對象之間產(chǎn)生關(guān)聯(lián)陡叠,那么這時候可以用到深拷貝玩郊。既然屬性值類型是數(shù)組和或象時只會傳址肢执,那么我們就用遞歸來解決這個問題枉阵,把父對象中所有屬于對象的屬性類型都遍歷賦給子對象即可。測試代碼如下:

/**
 * 深拷貝實現(xiàn)方法
 */
var a = { key1: "11111" }
function _copy(p, c) {
  var c = c || {};
  for (var i in p) {
    if (typeof p[i] === "object") {
      c[i] = (p[i].constructor === Array) ? [] : {}
      _copy(p[i], c[i]);
    } else {
      c[i] = p[i]
    }
  }
  return c;
}
a.key2 = ["小", "小"]
var b = {}
b = _copy(a, b);
b.key1 = "22"
b.key2.push("大");
console.log(a)
console.log(b)

輸出結(jié)果:

{ key1: '11111', key2: [ '小', '小' ] }
{ key1: '22', key2: [ '小', '小', '大' ] }

好了预茄,我們看下圖內(nèi)存中的賦值過程:

image.png

來個最后的總結(jié) : 基本數(shù)據(jù)類型和引用數(shù)據(jù)類型區(qū)別
1兴溜、聲明變量時內(nèi)存分配不同

  • 原始類型:在棧中,因為占據(jù)空間是固定的耻陕,可以將他們存在較小的內(nèi)存中-棧中拙徽,這樣便于迅速查詢變量的值
  • 引用類型:存在堆中,棧中存儲的變量诗宣,只是用來查找堆中的引用地址膘怕。
    這是因為:引用值的大小會改變,所以不能把它放在棧中召庞,否則會降低變量查尋的速度岛心。相反,放在變量的椑鹤疲空間中的值是該對象存儲在堆中的地址忘古。地址的大小是固定的,所以把它存儲在棧中對變量性能無任何負(fù)面影響

2诅诱、不同的內(nèi)存分配帶來不同的訪問機(jī)制
在javascript中是不允許直接訪問保存在堆內(nèi)存中的對象的髓堪,所以在訪問一個對象時,首先得到的是這個對象在堆內(nèi)存中的地址娘荡,然后再按照這個地址去獲得這個對象中的值干旁,這就是傳說中的按引用訪問。而原始類型的值則是可以直接訪問到的炮沐。

3争群、復(fù)制變量時的不同

  • 原始值:在將一個保存著原始值的變量復(fù)制給另一個變量時,會將原始值的副本賦值給新變量央拖,此后這兩個變量是完全獨(dú)立的祭阀,他們只是擁有相同的value而已鹉戚。
  • 引用值:在將一個保存著對象內(nèi)存地址的變量復(fù)制給另一個變量時,會把這個內(nèi)存地址賦值給新變量专控,
    也就是說這兩個變量都指向了堆內(nèi)存中的同一個對象抹凳,他們中任何一個作出的改變都會反映在另一個身上。
    (這里要理解的一點(diǎn)就是伦腐,復(fù)制對象時并不會在堆內(nèi)存中新生成一個一模一樣的對象赢底,只是多了一個保存指向這個對象指針的變量罷了)。多了一個指針

4柏蘑、參數(shù)傳遞的不同(把實參復(fù)制給形參的過程)
首先我們應(yīng)該明確一點(diǎn):ECMAScript中所有函數(shù)的參數(shù)都是按值來傳遞的幸冻。
但是為什么涉及到原始類型與引用類型的值時仍然有區(qū)別呢?還不就是因為內(nèi)存分配時的差別咳焚。

  • 原始值:只是把變量里的值傳遞給參數(shù)洽损,之后參數(shù)和這個變量互不影響。
  • 引用值:對象變量它里面的值是這個對象在堆內(nèi)存中的內(nèi)存地址革半,這一點(diǎn)你要時刻銘記在心碑定!
    因此它傳遞的值也就是這個內(nèi)存地址,這也就是為什么函數(shù)內(nèi)部對這個參數(shù)的修改會體現(xiàn)在外部的原因了又官,因為它們都指向同一個對象延刘。

參考博客:https://www.cnblogs.com/c2016c/articles/9328725.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市六敬,隨后出現(xiàn)的幾起案子碘赖,更是在濱河造成了極大的恐慌,老刑警劉巖外构,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件普泡,死亡現(xiàn)場離奇詭異,居然都是意外死亡典勇,警方通過查閱死者的電腦和手機(jī)劫哼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來割笙,“玉大人权烧,你說我怎么就攤上這事∩烁龋” “怎么了般码?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乱顾。 經(jīng)常有香客問我板祝,道長,這世上最難降的妖魔是什么走净? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任券时,我火速辦了婚禮孤里,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘橘洞。我一直安慰自己捌袜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布炸枣。 她就那樣靜靜地躺著虏等,像睡著了一般。 火紅的嫁衣襯著肌膚如雪适肠。 梳的紋絲不亂的頭發(fā)上霍衫,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音侯养,去河邊找鬼敦跌。 笑死,一個胖子當(dāng)著我的面吹牛沸毁,可吹牛的內(nèi)容都是我干的峰髓。 我是一名探鬼主播傻寂,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼息尺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了疾掰?” 一聲冷哼從身側(cè)響起搂誉,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎静檬,沒想到半個月后炭懊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拂檩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年侮腹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稻励。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡父阻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出望抽,到底是詐尸還是另有隱情加矛,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布煤篙,位于F島的核電站斟览,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辑奈。R本人自食惡果不足惜苛茂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一已烤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妓羊,春花似錦草戈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涨颜,卻和暖如春费韭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庭瑰。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工星持, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弹灭。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓督暂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親穷吮。 傳聞我的和親對象是個殘疾皇子逻翁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355