js深拷貝淺拷貝

目錄

一.數(shù)據(jù)類型

二.淺拷貝與深拷貝

三.賦值和淺拷貝的區(qū)別

四.淺拷貝的實現(xiàn)方式

五.深拷貝的實現(xiàn)方式


一.數(shù)據(jù)類型

數(shù)據(jù)分為基本數(shù)據(jù)類型(String, Number, Boolean, Null, Undefined卸夕,Symbol)和對象數(shù)據(jù)類型。
基本數(shù)據(jù)類型:
訪問:基本數(shù)據(jù)類型的值是按值訪問的。
存儲:基本類型的變量是存放在棧內(nèi)存(Stack)里的。
圖解 : 棧內(nèi)存中包括了變量的標(biāo)示符和變量的值。

引用數(shù)據(jù)類型的特點:存儲的是該對象在棧中引用挤庇,真實的數(shù)據(jù)存放在堆內(nèi)存里
引用數(shù)據(jù)類型在棧中儲存了指針拐格,該指針指向堆中該實體的起始地址吵取。當(dāng)解釋器尋找引用值時逐虚,會首先檢索其在棧中的地址,取得地址后從堆中獲取實體谆膳。
訪問:引用類型的值是按引用訪問的叭爱。
存儲:引用類型的值是保存在堆內(nèi)存(Heap)中的對象(Object)。
圖解:
棧內(nèi)存中包含了變量的標(biāo)示符和指向堆內(nèi)存中該對象的指針
堆內(nèi)存中包含了對象的內(nèi)容

image

JavaScript 不能直接操作對象的內(nèi)存空間(堆內(nèi)存)

一.淺拷貝與深拷貝

深拷貝和淺拷貝是只針對Object和Array這樣的引用數(shù)據(jù)類型的漱病。
深拷貝和淺拷貝的示意圖大致如下:

image

淺拷貝只復(fù)制指向某個對象的指針买雾,而不是復(fù)制對象本身 新舊對象還是共享同一塊內(nèi)存。
但深拷貝會另外創(chuàng)造一個一摸一樣的對象杨帽, 新對象跟原對象不共享內(nèi)存漓穿,修改新對象不會改到原對象。

三.賦值和淺拷貝的區(qū)別

  • 當(dāng)我們把一個對象賦值給一個新的變量時注盈,賦的其實是該對象的在棧中的地址晃危,而不是堆中的數(shù)據(jù)姊舵。也就是兩個對象指向的是同一個存儲空間,無論哪個對象發(fā)生改變孟抗,其實都是改變的存儲空間的內(nèi)容疫诽,因此,兩個對象是聯(lián)動的鳍鸵。

  • 淺拷貝是按位拷貝對象苇瓣,它會創(chuàng)建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝偿乖。如果屬性是基本類型击罪,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型)贪薪,拷貝的就是內(nèi)存地址 媳禁,因此如果其中一個對象改變了這個地址,就會影響到另一個對象古掏。即默認(rèn)拷貝構(gòu)造函數(shù)只是對對象進行淺拷貝復(fù)制(逐個成員依次拷貝)损话,即只復(fù)制對象空間而不復(fù)制資源。

// 對象賦值
 var obj1 = {
    'name' : 'zhangsan',
    'age' :  '18',
    'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)
image
// 淺拷貝
 var obj1 = {
    'name' : 'zhangsan',
    'age' :  '18',
    'language' : [1,[2,3],[4,5]],
};
 var obj3 = shallowCopy(obj1);
 obj3.name = "lisi";
 obj3.language[1] = ["二","三"];
 function shallowCopy(src) {
    var dst = {};
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {
            dst[prop] = src[prop];
        }
    }
    return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)
image

上面例子中槽唾,obj1是原始數(shù)據(jù)丧枪,obj2是賦值操作得到,而obj3淺拷貝得到庞萍。我們可以很清晰看到對原始數(shù)據(jù)的影響拧烦,具體請看下表:


image

四.淺拷貝的實現(xiàn)方式

1.Object.assign()
Object.assign() 方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標(biāo)對象,然后返回目標(biāo)對象钝计。但是 Object.assign()進行的是淺拷貝恋博,拷貝的是對象的屬性的引用,而不是對象本身私恬。

var obj = { a: {a: "xxxc", b: 19} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wewd";
console.log(obj.a.a); //wewd

注意:當(dāng)object只有一層的時候债沮,是深拷貝

let obj = {
    username: 'jsx'
    };
let obj2 = Object.assign({},obj);
obj2.username = 'wawa';
console.log(obj);//{username: "jsx"}

2.Array.prototype.concat()

let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);

修改新對象會改到原對象:

example

3.Array.prototype.slice()

let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

修改新對象會改到原對象:


image

Array的slice和concat方法不修改原數(shù)組,只會返回一個淺復(fù)制了原數(shù)組中的元素的一個新數(shù)組本鸣。
原數(shù)組的元素會按照下述規(guī)則拷貝:

  • 如果該元素是個對象引用(不是實際的對象)疫衩,slice 會拷貝這個對象引用到新的數(shù)組里。兩個對象引用都引用了同一個對象荣德。如果被引用的對象發(fā)生改變闷煤,則新的和原來的數(shù)組中的這個元素也會發(fā)生改變。
  • 對于字符串涮瞻、數(shù)字及布爾值來說(不是 String鲤拿、Number 或者 Boolean 對象),slice 會拷貝這些值到新的數(shù)組里署咽。在別的數(shù)組里修改這些字符串或數(shù)字或是布爾值近顷,將不會影響另一個數(shù)組。
let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[1] = 2
console.log(arr,arr3);
image

五.深拷貝的實現(xiàn)方式

1.遞歸方法實現(xiàn)深度克隆原理:遍歷對象、數(shù)組直到里邊都是基本數(shù)據(jù)類型幕庐,然后再去復(fù)制久锥,就是深度拷貝

var deepCopy = function(obj) {
    if (typeof obj !== 'object') return obj //判斷不是object就直接返回
    var newObj = (Object.prototype.toString.call(obj) === '[object Array]') ? [] : {} //判斷復(fù)制的目標(biāo)是數(shù)組還是對象
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = (typeof obj[key] !== 'object') ? obj[key]: deepCopy(obj[key]) // 如果值是對象,就遞歸一下异剥。 如果不是瑟由,就直接賦值
        }
    }
    return newObj
}

就這么簡單。
類似的方法都是挺雷同的

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判斷復(fù)制的目標(biāo)是數(shù)組還是對象
  for(let keys in source){ // 遍歷目標(biāo)
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 如果值是對象冤寿,就遞歸一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 如果不是歹苦,就直接賦值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}  

/*原理都一樣*/
var  deepClone=function(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

2.除此之外還有 函數(shù)庫 lodash
該函數(shù)庫也有提供_.cloneDeep用來做 Deep Copy

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false

JSON.parse(JSON.stringfify(obj))
3.JSON.parse(JSON.stringfify(obj))
原理:
用JSON.stringify將對象轉(zhuǎn)成JSON字符串,
再用JSON.parse()把字符串解析成對象督怜,一去一來殴瘦,新的對象產(chǎn)生了,而且對象會開辟新的棧号杠,實現(xiàn)深拷貝蚪腋。
可以實現(xiàn)數(shù)組或?qū)ο笊羁截?但不能處理函數(shù)
因為JSON.stringify() 方法是將一個JavaScript值(對象或者數(shù)組)轉(zhuǎn)換為一個 JSON字符串,不能接受函數(shù)

可以自行試驗 就不做圖例了姨蟋,太懶了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屉凯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子眼溶,更是在濱河造成了極大的恐慌悠砚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堂飞,死亡現(xiàn)場離奇詭異灌旧,居然都是意外死亡,警方通過查閱死者的電腦和手機绰筛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門枢泰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铝噩,你說我怎么就攤上這事宗苍。” “怎么了薄榛?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長让歼。 經(jīng)常有香客問我敞恋,道長,這世上最難降的妖魔是什么谋右? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任硬猫,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啸蜜。我一直安慰自己坑雅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布衬横。 她就那樣靜靜地躺著裹粤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜂林。 梳的紋絲不亂的頭發(fā)上遥诉,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音噪叙,去河邊找鬼矮锈。 笑死,一個胖子當(dāng)著我的面吹牛睁蕾,可吹牛的內(nèi)容都是我干的苞笨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼子眶,長吁一口氣:“原來是場噩夢啊……” “哼瀑凝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起壹店,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤猜丹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后硅卢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體射窒,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年将塑,在試婚紗的時候發(fā)現(xiàn)自己被綠了脉顿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡点寥,死狀恐怖艾疟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敢辩,我是刑警寧澤蔽莱,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站戚长,受9級特大地震影響盗冷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜同廉,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一仪糖、第九天 我趴在偏房一處隱蔽的房頂上張望柑司。 院中可真熱鬧,春花似錦锅劝、人聲如沸攒驰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玻粪。三九已至,卻和暖如春稠集,著一層夾襖步出監(jiān)牢的瞬間奶段,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工剥纷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痹籍,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓晦鞋,卻偏偏與公主長得像蹲缠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子悠垛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 項目中會遇到的體現(xiàn)线定。對一個table進行操作后,淺拷貝引用的form一起被改變确买。深拷貝和淺拷貝簡單解釋淺拷貝和深拷...
    aatter閱讀 359評論 0 0
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,089評論 1 32
  • 什么是深拷貝斤讥,什么是淺拷貝 說到深淺拷貝,就不得不提到另外一個知識點湾趾,那就是引用類型和基本類型以及堆和棧的區(qū)別芭商。再...
    jeff_nz閱讀 843評論 0 0
  • 先一句話說清楚铛楣,深拷貝、淺拷貝探討的是嵌套對象的對象艺普。 接著來看看深拷貝簸州、淺拷貝的異同:1.無論哪種拷貝,原對象和...
    皮卡丘_小面包閱讀 413評論 0 0
  • 坐在寂寞的角落, 你不理我歧譬, 我不怪你岸浑。 因為我卑微的身份。 不瑰步, 誰說農(nóng)婦的身份就低微助琐, 那是從前的思維。 今天...
    鄉(xiāng)村奶奶閱讀 199評論 4 8