前幾天偶然看到了一個這樣的題: 這個題代碼不多,不過考察的知識點卻非常不錯。我們知道記憶體空間分為棧記憶體和堆記憶體。棧記憶體用來存放供js代碼來執行的環境,所以為了保證性能減少記憶體占用,我們一般把占用空間較小的類似於基本數據類型放在棧記憶體中,像引用數據類型這種占據空間比較大的東西,我們需要將它放在一個貯藏 ...
前幾天偶然看到了一個這樣的題:
1 <script> 2 var a={n:1}; 3 var b=a; 4 a.x=a={n:2};//關鍵代碼
5 console.log(a.x);//undefined
6 console.log(b.x);//[object Object]
7 </script>
這個題代碼不多,不過考察的知識點卻非常不錯。我們知道記憶體空間分為棧記憶體和堆記憶體。棧記憶體用來存放供js代碼來執行的環境,所以為了保證性能減少記憶體占用,我們一般把占用空間較小的類似於基本數據類型放在棧記憶體中,像引用數據類型這種占據空間比較大的東西,我們需要將它放在一個貯藏室中—堆記憶體中,堆記憶體相較於棧記憶體對js執行時對性能影響很小,雖然引用類型存放在堆記憶體里了,但是當我們代碼執行時我們也需要用到這部分,所以我們需要將類似於標簽,名字(類似於元素的ID)放置於棧記憶體中,和堆記憶體中的東西一一對應,當執行到他時直接去堆記憶體中找,這種模式就好比古代的銀票和同等價值的黃金和白銀,一個很有錢的商人不能每次出門都推著一車銀子吧,那可是金屬,那這樣有錢人活的就太累了,所以一個等面值的銀票揣在手中,一切問題就搞定了。 所以“不同變數的同一引用數據類型賦值就是指向的是同一個堆記憶體空間,不會去創造新的記憶體空間,不然太浪費空間了,你要知道這可是記憶體,記憶體呀!!”
1. 像此題中一樣 var a={n:1}; var b=a; 這是並沒有給變數b在堆記憶體中在創建一個對象{n:1},而是他倆共同指向記憶體空間中的{n:1},不論你創建多少個變數去賦值堆記憶體中始只存在一個{n:1};我們姑且稱呼這個對象為A,草圖如下:
2. 接下來我們可以看到此題中有標註‘關鍵代碼’字樣的,他們考察了我們連續賦值,並且還有屬性操作:
a.x = a = {n:2};
js的賦值運算順序永遠都是從右往左的,不過由於“.”是優先順序最高的運算符,所以這行代碼先“計算”了a.x。
所以我們先進行屬性操作,a.x,因為此時x作為a的一個屬性,沒有最開始沒有從右向左賦值時它的值為undefined,這行代碼可以說已經執行了a.x,這時如下圖
接下來從右向左開始賦值,將a的指向從對象A指向了對象B{n:2},
接下來繼續執行 a.x=a,很多人會認為這裡是“對象B也新增了一個屬性x,並指向對象B自己”,很多人這樣認為是錯誤的,根據優先性( . 運算符最先計算),咱們剛纔已經計算過a. x,那時變數a的指向還是對象A,變數a指針並沒有發生變化,所以此時a.x確切地說應該是對象A.x,代碼再次執行時,指針發生了變化,a的指針指向B,此時a.x=a再賦值時不再次執行依次a.x然後再賦值,這樣系統不允許,而是直接賦值,相當於之前的A.x賦值,所以 a.x=a 應理解為對象A的屬性x指向了對象B:
那麼根據圖表很容易得出結果當console.log(a.x)的時候,a是指向對象B的,但對象B沒有屬性x。沒關係,當查找一個對象的屬性時,JavaScript 會向上遍歷原型鏈,直到找到給定名稱的屬性為止。但當查找到達原型鏈的頂部 - 也就是 Object.prototype - 仍然沒有找到指定的屬性B.prototype.x,自然也就輸出undefined;
而在console.log(b.x)的時候,由於b.x表示對象A的x屬性,該屬性是指向對象B,自然也輸出了[object Object]了,註意這裡的[object Object]可不是2個對象的意思,對象的字元串形式,是隱式調用了Object對象的toString()方法,形式是:"[object Object]"。
為了驗證咱們的結果我們可以將上面的題進行變種:
1 <script> 2 var a={n:1}; 3 var b=a; 4 a={n:2} 5 a.x=a; 6 console.log(a.x);//[object,Object] 7 console.log(b.x);//undefined
8 </script>
那麼與上題的區別就是我們將變數a指針先改變,然後在進行對象屬性的操作a.x。
嘿嘿,如果我們這樣連等呢?
1 <script> 2 var a={n:1}; 3 var b=a; 4 a=a.x={n:2}; 5 console.log(a.x); 6 console.log(b.x); 7 console.log(a.n); 8 console.log(b.n); 9 </script>
此時的結果是和第一題的解題思路是一樣的,只要是既有變數和變數屬性的連等賦值的列印元素屬性的值都為undefined
總結:所以我們做此類題時無外乎考慮這幾個知識點連等賦值的優先順序別從右向左,不過遇見屬性一切玩完,
我驗證了一下凡是類似於這樣變數a=a.x=引用數據類型,都可以引用只要是既有變數和變數屬性的連等賦值的列印元素屬性的值都為undefined。