詳解JavaScript的內(nèi)存空間稿饰、賦值和深淺拷貝

JavaScript的內(nèi)存空間

在JavaScript中捐名,每一個(gè)數(shù)據(jù)都需要一個(gè)內(nèi)存空間。內(nèi)存空間分為兩種谜嫉,棧內(nèi)存(stack)與堆內(nèi)存(heap)

棧是系統(tǒng)自動(dòng)分配的內(nèi)存空間萎坷,由系統(tǒng)自動(dòng)釋放,堆則是動(dòng)態(tài)分配的內(nèi)存沐兰,大小不定不會(huì)自動(dòng)釋放哆档。

基礎(chǔ)數(shù)據(jù)類型

JavaScript中的基礎(chǔ)數(shù)據(jù)類型,這些值都有固定的大小,保存在棧內(nèi)存中,由系統(tǒng)自動(dòng)分配存儲(chǔ)空間在棧內(nèi)存空間的值拆檬,我們可以直接進(jìn)行操作钥弯,因此基礎(chǔ)數(shù)據(jù)類型都是按照值訪問(wèn)

在棧內(nèi)存中的數(shù)據(jù)發(fā)生復(fù)制的行為時(shí)莫其,系統(tǒng)會(huì)自動(dòng)為新變量開辟一個(gè)新的內(nèi)存空間,當(dāng)復(fù)制執(zhí)行后,兩個(gè)內(nèi)存空間的值就互不影響,改變其中一個(gè)不會(huì)影響另一個(gè)

棧內(nèi)存空間數(shù)據(jù)復(fù)制示例
var a = `I am variable a`;
var b = a; 
console.log(b); //`I am variable a`
b = `I am variable b`;
console.log(a); //`I am variable a`
console.log(b); //`I am variable b`

引用數(shù)據(jù)類型

引用類型的值是保存在堆內(nèi)存中的對(duì)象雇寇,在JavaScript中我們不能直接操作對(duì)象的堆內(nèi)存空間。因?yàn)橐妙愋偷闹刀际前匆迷L問(wèn)的蚌铜,所以在操作對(duì)象時(shí)锨侯,實(shí)際上是操作對(duì)象的引用而不是實(shí)際的對(duì)象。引用可以理解為保存在棧內(nèi)存中的一個(gè)地址冬殃,該地址指向堆內(nèi)存中的一個(gè)實(shí)際對(duì)象

引用類型值的復(fù)制囚痴,系統(tǒng)會(huì)為新的變量自動(dòng)分配一個(gè)新的棧內(nèi)存空間這個(gè)棧內(nèi)存空間保存著與被復(fù)制變量相同的指針,盡管他們?cè)跅?nèi)存中的內(nèi)存空間的位置互相獨(dú)立但是在堆內(nèi)存中訪問(wèn)到的對(duì)象實(shí)際上是同一個(gè)审葬,因此深滚,當(dāng)我們改變其中一個(gè)對(duì)象的值時(shí)奕谭,實(shí)際上就是改變?cè)瓉?lái)的對(duì)象

棧內(nèi)存空間保存指針(地址),堆內(nèi)存空間保存實(shí)際的對(duì)象痴荐,我們通過(guò)變量訪問(wèn)對(duì)象時(shí)血柳,實(shí)際上訪問(wèn)的是對(duì)象的引用(地址)

內(nèi)存中的棧區(qū)域存放變量(基本類型的變量包括變量聲明和值)以及指向堆區(qū)域存儲(chǔ)位置的指針(引用類型的變量包括變量聲明和指向內(nèi)容的指針)

var a = {
  name : `I am object a`,
  type : 'object'
}
 
var b = a;
console.log(b);
// {name: "I am object a", type: "object"}
 
b.name = `I am object b`;
 
console.log(a);
// {name: "I am object b", type: "object"}
 
console.log(b);
 
// {name: "I am object b", type: "object"}

基本類型總結(jié)

基本數(shù)據(jù)類型:

包括:null、undefined蹬昌、number混驰、string攀隔、boolean皂贩、symbol(es6)

存放位置:內(nèi)存中的棧區(qū)域中

比較:值的比較,判斷是否相等昆汹,如果值相等明刷,就相等。一般使用===進(jìn)行比較满粗,因?yàn)?=會(huì)進(jìn)行類型的轉(zhuǎn)換

拷貝:賦值(通過(guò)(=)賦值操作符 賦值)辈末,賦值完成后,兩個(gè)變量之間就沒有任何關(guān)系了映皆,改變其中一個(gè)變量的值對(duì)另一個(gè)沒有任何影響

引用類型總結(jié)

引用數(shù)據(jù)類型:

包括:數(shù)組挤聘、對(duì)象、函數(shù)

存放位置:內(nèi)存的棧區(qū)域中存放變量和指針捅彻,堆區(qū)域存儲(chǔ)實(shí)際的對(duì)象

比較:是引用的比較(就是地址的比較组去,變量在棧內(nèi)存中對(duì)應(yīng)的指針地址相等就指向同一個(gè)對(duì)象)判斷是否為同一個(gè)對(duì)象,示例如下

變量a和變量b的引用不同步淹,對(duì)象就不是同一個(gè)對(duì)象
var a = {name:'Jay'};
var b = {name:'Jay'};
a===b //false

我們對(duì)JavaScript中引用類型進(jìn)行操作的時(shí)候从隆,都是操作其對(duì)象的引用(保存在棧內(nèi)存中的指針)

賦值、深拷貝和淺拷貝 (Assignment, deep copy and shallow copy)

賦值:兩個(gè)變量的值(指針)都指向同一個(gè)對(duì)象缭裆,改變其中一個(gè)键闺,另一個(gè)也會(huì)受到影響

所謂拷貝就是復(fù)制,通過(guò)復(fù)制原對(duì)象生成一個(gè)新的對(duì)象

淺拷貝:重新在堆內(nèi)存中開辟一個(gè)空間澈驼,拷貝后新對(duì)象獲得一個(gè)獨(dú)立的基本數(shù)據(jù)類型數(shù)據(jù)辛燥,和原對(duì)象共用一個(gè)原對(duì)象內(nèi)的引用類型數(shù)據(jù),改變基本類型數(shù)據(jù)缝其,兩個(gè)對(duì)象互不影響挎塌,改變其中一個(gè)對(duì)象內(nèi)的引用類型數(shù)據(jù),另一個(gè)對(duì)象會(huì)受到影響

var obj = {
  name: 'Jay Chou',
  age: 32,
  song:{
    name:'發(fā)如雪',
    year:2007
  }
}
var obj1 = obj;
function shallowCopy(obj){
  var scObj = {};
  for(var prop in obj){
    if(obj.hasOwnProperty(prop)){
      scObj[prop] = obj[prop]
    }
  }
  return scObj;
}
var obj2 = shallowCopy(obj);
console.log(obj === obj1,'obj === obj1','賦值');
console.log(obj === obj2,'obj === obj2','淺拷貝');
// true "obj === obj1" "賦值"
// false "obj === obj2" "淺拷貝"
console.log(obj.song === obj2.song);
//true
obj2.song.name='雙截棍';
obj2.name='Jay';
console.log(obj)
// {name: "Jay Chou", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj1);
// {name: "Jay Chou", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj2);
{name: "Jay", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj===obj1)
//true
console.log(obj===obj2)
//false

深拷貝:不論是對(duì)象內(nèi)的基本類型還是引用類型都被完全拷貝,拷貝后兩個(gè)對(duì)象互不影響
1.乞丐版的深拷貝 JSON.stringify()以及JSON.parse()

var obj1 = {
    a: 1,
    b: 2,
    c: 3
}
var objString = JSON.stringify(obj1);
var obj2 = JSON.parse(objString);
obj2.a = 5;
console.log(obj1.a);  // 1
console.log(obj2.a); // 5

可以看到?jīng)]有發(fā)生引用問(wèn)題氏淑,修改obj2的數(shù)據(jù)勃蜘,并不會(huì)對(duì)obj1造成任何影響
但是為什么說(shuō)它是乞丐版的呢?
那是因?yàn)?使用JSON.stringify()以及JSON.parse()它是不可以拷貝 undefined 假残, function缭贡, RegExp 等等類型的
2.接著來(lái)看第二種方式 Object.assign(target, source)(這其實(shí)也是淺拷貝的一種)

var obj1 = {
    a: 1,
    b: 2,
    c: 3
}
var obj2 = Object.assign({}, obj1);
obj2.b = 5;
console.log(obj1.b); // 2
console.log(obj2.b); // 5

第二種方式實(shí)現(xiàn)的看起來(lái)也沒有任何的問(wèn)題炉擅,但是這是一層對(duì)象,如果是有多層嵌套呢

 var obj1 = {
    a: 1,
    b: 2,
    c: ['a','b','c']
}
var obj2 = Object.assign({}, obj1);
obj2.c[1] = 5;
console.log(obj1.c); // ["a", 5, "c"]
console.log(obj2.c); // ["a", 5, "c"]

可以看到對(duì)于一層對(duì)象來(lái)說(shuō)是沒有任何問(wèn)題的阳惹,但是如果對(duì)象的屬性對(duì)應(yīng)的是其它的引用類型的話谍失,還是只拷貝了引用,修改的話還是會(huì)有問(wèn)題
3.第三種方式 遞歸拷貝

// 定義一個(gè)深拷貝函數(shù)  接收目標(biāo)target參數(shù)
function deepClone(target) {
    // 定義一個(gè)變量
    let result;
    // 如果當(dāng)前需要深拷貝的是一個(gè)對(duì)象的話
    if (typeof target === 'object') {
    // 如果是一個(gè)數(shù)組的話
        if (Array.isArray(target)) {
            result = []; // 將result賦值為一個(gè)數(shù)組莹汤,并且執(zhí)行遍歷
            for (let i in target) {
                // 遞歸克隆數(shù)組中的每一項(xiàng)
                result.push(deepClone(target[i]))
            }
         // 判斷如果當(dāng)前的值是null的話快鱼;直接賦值為null
        } else if(target===null) {
            result = null;
         // 判斷如果當(dāng)前的值是一個(gè)RegExp對(duì)象的話,直接賦值    
        } else if(target.constructor===RegExp){
            result = target;
        }else {
         // 否則是普通對(duì)象纲岭,直接for in循環(huán)抹竹,遞歸賦值對(duì)象的所有值
            result = {};
            for (let i in target) {
                result[i] = deepClone(target[i]);
            }
        }
     // 如果不是對(duì)象的話,就是基本數(shù)據(jù)類型止潮,那么直接賦值
    } else {
        result = target;
    }
     // 返回最終結(jié)果
    return result;
}

可以看一下效果

  let obj1 = {
        a: {
            c: /a/,
            d: undefined,
            b: null
        },
        b: function () {
            console.log(this.a)
        },
        c: [
            {
                a: 'c',
                b: /b/,
                c: undefined
            },
            'a',
            3
        ]
    }
    let obj2 = deepClone(obj1);
        console.log(obj2);

可以看到最終拷貝的結(jié)果是null窃判、undefinde、function喇闸、RegExp等特殊的值也全部拷貝成功了袄琳,而且我們修改里邊的值也不會(huì)有任何問(wèn)題的
到這里我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的深拷貝,當(dāng)然燃乍,我的這個(gè)也只是簡(jiǎn)單實(shí)現(xiàn)一下唆樊,還有很多問(wèn)題沒有解決,只是給您提供一個(gè)思路

比較:賦值刻蟹、深拷貝逗旁、淺拷貝

賦值:新對(duì)象仍然指向原對(duì)象,改變新對(duì)象的基本類型和引用類型的值都會(huì)使原對(duì)象對(duì)應(yīng)的值一同改變

淺拷貝:改變新對(duì)象基本類型的值不會(huì)使原對(duì)象對(duì)應(yīng)的值一起改變座咆,但是改變新對(duì)象引用類型的值會(huì)使原對(duì)象對(duì)應(yīng)的值一同改變

深拷貝:改變新對(duì)象基本類型和引用類型的值痢艺,都不會(huì)影響原對(duì)象,兩者互相獨(dú)立介陶,互不影響

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末堤舒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哺呜,更是在濱河造成了極大的恐慌舌缤,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件某残,死亡現(xiàn)場(chǎng)離奇詭異国撵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)玻墅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門介牙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人澳厢,你說(shuō)我怎么就攤上這事环础∏羲疲” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵线得,是天一觀的道長(zhǎng)饶唤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)贯钩,這世上最難降的妖魔是什么募狂? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮角雷,結(jié)果婚禮上祸穷,老公的妹妹穿的比我還像新娘。我一直安慰自己谓罗,他們只是感情好粱哼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布季二。 她就那樣靜靜地躺著檩咱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胯舷。 梳的紋絲不亂的頭發(fā)上刻蚯,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音桑嘶,去河邊找鬼炊汹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛逃顶,可吹牛的內(nèi)容都是我干的讨便。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼以政,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霸褒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起盈蛮,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤废菱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后抖誉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殊轴,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年袒炉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旁理。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡我磁,死狀恐怖孽文,靈堂內(nèi)的尸體忽然破棺而出淹接,到底是詐尸還是另有隱情,我是刑警寧澤叛溢,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布塑悼,位于F島的核電站,受9級(jí)特大地震影響楷掉,放射性物質(zhì)發(fā)生泄漏厢蒜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一烹植、第九天 我趴在偏房一處隱蔽的房頂上張望斑鸦。 院中可真熱鬧,春花似錦草雕、人聲如沸巷屿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嘱巾。三九已至,卻和暖如春诫钓,著一層夾襖步出監(jiān)牢的瞬間旬昭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工菌湃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留问拘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓惧所,卻偏偏與公主長(zhǎng)得像骤坐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子下愈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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