資料儲存位置記憶體圖及一個題目

YyzclYang發表於2018-05-14

最近學習的時候看到一位老師講了一道題,提到了資料在記憶體中的儲存位置,個人感覺收穫挺大了,來分享一下
題目為

var a = {n: 1};
var b = a;
a.x = a = {n: 2};

a.x 
b.x 
複製程式碼

在分析這道題之前,想先來說說記憶體圖,就是資料在記憶體中的儲存方式及位置

棧記憶體與堆記憶體

JavaScript的資料型別有NumberStringBooleannullundefinedobject(暫時不考慮symbol),前五者稱為基本型別,它們直接存放在棧記憶體中,而object稱為複雜型別,它在棧記憶體中只存放了一個地址,地址指向堆記憶體中object資料的儲存地址,例

var n = 1;
var b = true;
var obj = {name: 'yyzcl'};
複製程式碼

記憶體圖如下所示

資料儲存位置記憶體圖及一個題目

從圖中可以看到,基本型別的值存放在棧記憶體中,而複雜型別的資料其實是放在堆記憶體中,在棧記憶體中只存放了一個地址指向對記憶體中的資料

幾個例子

第一題

var a = 1;
var b = a;
b = 2;

a // 1
複製程式碼

記憶體圖表示為

資料儲存位置記憶體圖及一個題目

從圖中可以看到,在var a = 1;b = a;執行後,ab都為1,而又執行了b = 2;b的值被改變了,這個改變直接發生在棧記憶體中,b的改變並未影響到a,所以a的值還是1

第二題

var a = {name: 'yyzcl'};
var b = a;
b = {name: '123'};

a.name // "yyzcl"
複製程式碼

記憶體圖表示為

資料儲存位置記憶體圖及一個題目

在執行前兩句定義之後,ab都在棧記憶體中擁有一個相同的地址,指向堆記憶體中的真實資料地址,而後給b重新賦值一個新物件時,堆記憶體中會生成一個新物件資料,b在棧記憶體儲存的地址會發生改變,指向堆記憶體的新物件。但此時a的棧記憶體值未發生改變,堆記憶體的物件資料也未被改變

第三題

var a = {name: 'yyzcl'};
var b = a;
b.name = '123';

a.name // "123"
複製程式碼

記憶體圖表示為

資料儲存位置記憶體圖及一個題目

這裡和2有一點點區別,就是b在宣告之後,只給b其中的一個value重新賦值了,這時候就會改變堆記憶體中的資料,而a在棧記憶體存放的地址也指向這個資料,所以a的資料也跟著被改變了

第四題

var a = {name: 'yyzcl'};
var b = a;
b = null;

a //{name: 'yyzcl'}
複製程式碼

記憶體圖表示為

資料儲存位置記憶體圖及一個題目

從程式碼中可以知道,ab在宣告之後,重新給b賦值一個基本型別的資料,所以直接改變了b在棧記憶體中的值,將之前存放的堆記憶體地址更換為nullb與堆記憶體的資料斷開聯絡,而a及堆記憶體資料並未發生改變

重新看那道題

var a = {name: 'yyzcl'};
var b = a;
a.x = a = {name: '123'};

a.x // undefined
b.x // [object Object]
複製程式碼

其實要弄清楚這道題,光知道資料在記憶體的儲存方式是不夠的,還需要知道JavaScript運算子的優先順序。在JavaScript中,等號的運算其實是從右向左的。我們一步步來看ab的變化
首先是宣告完成之後的狀態

資料儲存位置記憶體圖及一個題目

ab的棧記憶體資料相同,都指向堆記憶體中的資料

再來看這一句a.x = a = {name: '123'};
首先是從左向右,看a.x,記憶體圖變化為
資料儲存位置記憶體圖及一個題目

從圖中可以看到,在a所指的堆記憶體資料中新增了一個key,其對應的value還未定義,此時a.x表示的棧記憶體地址還是ADDR 101(這很關鍵 !!!!!

再看a = {name: '123'},這段沒什麼好說的,就是在堆記憶體中新生成一個物件,並用a在棧記憶體中的地址資料指向它,記憶體圖表示為
資料儲存位置記憶體圖及一個題目

接下來就是重點
這裡讓大家判斷一下此時a.x在棧記憶體中存放的地址

A. ADDR 101
B. ADDR 200

正確結果是A,沒錯,此時a.x表示的地址是ADDR101,而後面那個a的地址已經變成了ADDR200,所以此時語句可以這麼理解

a.x(ADDR 101) = a(ADDR 200) = {name: '123'}

所以記憶體圖變化為

資料儲存位置記憶體圖及一個題目

才有

a.x // undefined
b.x // [object Object]
複製程式碼

a指向的是{name: '123'},根本沒有x屬性,所以是undefined
而b指向堆記憶體中ADDR 101的物件,其中x的值是一個地址ADDR 200,指向{name: '123'}這個物件,所以值為[object Object]
要弄清楚這個問題關鍵要知道複雜型別的資料在記憶體中的存放方式以及JavaScript運算子的優先順序

如有錯誤之處,還望不吝指教

相關文章