最近學習的時候看到一位老師講了一道題,提到了資料在記憶體中的儲存位置,個人感覺收穫挺大了,來分享一下
題目為
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
a.x
b.x
複製程式碼
在分析這道題之前,想先來說說記憶體圖,就是資料在記憶體中的儲存方式及位置
棧記憶體與堆記憶體
JavaScript的資料型別有Number
、String
、Boolean
、null
、undefined
和object
(暫時不考慮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;
執行後,a
和b
都為1
,而又執行了b = 2;
,b
的值被改變了,這個改變直接發生在棧記憶體中,b
的改變並未影響到a
,所以a
的值還是1
。
第二題
var a = {name: 'yyzcl'};
var b = a;
b = {name: '123'};
a.name // "yyzcl"
複製程式碼
記憶體圖表示為
在執行前兩句定義之後,
a
和b
都在棧記憶體中擁有一個相同的地址,指向堆記憶體中的真實資料地址,而後給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'}
複製程式碼
記憶體圖表示為
從程式碼中可以知道,
a
、b
在宣告之後,重新給b
賦值一個基本型別的資料,所以直接改變了b
在棧記憶體中的值,將之前存放的堆記憶體地址更換為null
,b
與堆記憶體的資料斷開聯絡,而a
及堆記憶體資料並未發生改變
重新看那道題
var a = {name: 'yyzcl'};
var b = a;
a.x = a = {name: '123'};
a.x // undefined
b.x // [object Object]
複製程式碼
其實要弄清楚這道題,光知道資料在記憶體的儲存方式是不夠的,還需要知道JavaScript運算子的優先順序。在JavaScript中,等號的運算其實是從右向左的。我們一步步來看a
和b
的變化
首先是宣告完成之後的狀態
a
和b
的棧記憶體資料相同,都指向堆記憶體中的資料再來看這一句
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運算子的優先順序
如有錯誤之處,還望不吝指教