【本週面試題】第4周 - 內存機制相關

1、解釋一下內存泄露是什麼意思?

2018-12-03 20:57:24javascript

 

內存泄漏指任何對象在您再也不擁有或須要他以後,其仍然存在內存中。html

也就是程序中分配的堆內存空間沒有被及時釋放或沒法釋放,致使的內存佔用過多,形成程序運行速度減慢甚至卡頓、崩潰。前端

在瀏覽器中識別內存泄漏的方法:java

  • 打開開發者工具,選擇 Memory
  • 在右側的Select profiling type字段裏面勾選 timeline
  • 點擊左上角的錄製按鈕。
  • 在頁面上進行各類操做,模擬用戶的使用狀況。
  • 一段時間後,點擊左上角的 stop 按鈕,面板上就會顯示這段時間的內存佔用狀況。

一段」高清「gif展現給你:面試

 

詳見:【進階1-4期】JavaScript深刻之帶你走進內存機制數組

2、堆棧存儲數據的特色 相關面試題:

一共給本身出了三道題:瀏覽器

1. 基本類型值的拷貝:bash

1 var a = 1; 2 var b = a; 3 console.log(b); 4 b = 2; 5 console.log(a);

 

2. 引用類型值的拷貝:(淺拷貝)數據結構

1 var c = { 2   n: 1
3 } 4 var d = c; 5 d.n = 3; 6 console.log(c.n);

 

3. 基本類型值的拷貝:【經典】閉包

1 var e = { 2   n: 1
3 } 4 var f = e; 5 e.x = e = { n: 4}; 6 console.log(e.x); 7 console.log(f);

 

 

答案和解析:

第一題打印:

> 1

> 1

首先第一個b彈出1,毫無疑問。能夠理解成b = a = 1。

實際上他是執行:

var b; 給b建立一塊空間

b = a;先查找a的值(1),再賦予給b變量。

而後第二個,依舊彈出1。

以前你理解b和a相等了,b變成2是否是a也要變成2啊?不是的,由於b和a不是一塊空間。

通俗點理解,就像是拷貝了a的一個副本給b。此時咱們修改(副本)b的值,對a毫無影響。

因此儘管b爲2了,a仍是1。

 

第二題打印:

> 3

剛看完上邊說a和b不影響,再看這裏是否是懵逼了。說好的不影響,怎麼d.n改爲了3,c.n也改爲了3呢?

其實啊,這就是堆棧空間存值的不一樣和拷貝堆內存數據的特色了:

首先要知道,堆棧空間存的值不一樣:

var c = {}; 定義一個對象c。

js引擎要作的事情以下:

  • var c; //棧內存開闢一塊空間放c
  • {n: 1}; // 堆內存開闢一塊空間放對象({n: 1}能夠當成一本書,書架就是堆空間,就好像在書架上放了一本書)
  • c = {n: 1}; 棧中找到c,賦予對象所在堆內存的地址。(拿書的索書號給了c,並非書自己給了c。索書號就是對象在堆空間的地址)
  • 而後, var d; // 同c,給d開闢一塊空間放棧內存
  • d = c; // 獲取c的值(就是那個索書號地址),copy一份給d。d拿到的一樣是c的副本。可是這個副本是一個指針,兩者同時指向堆空間的{n: 1};

以後的代碼,運行d.n = 3。就是把堆內存中惟一一處的那個{n:1}對象的n值給修改了。

此時,堆內存中{n:1}這個對象,如今變成了{n:3}。

 

第三題打印:

> undefined

> 一個對象:展開以下:

{ n: 1, x: { n:4 } }

 

此題是一個經典面試題,

首先第1-4行,和第二題定義c一個套路。

棧空間存了變量e和指針,堆空間存了對象{n:1};將e的指針再賦予f。

關鍵是第5行,這裏若是你按從左向右順序理解的話結果應該是:

各類可能

 

但這道題的坑點就在於js不是按照你想的、你閱讀的順序執行的。

首先,js中正確的賦值順序是從右向左的。

也就是應該先從最後邊開始,先將等號右邊的賦值給等號左邊。

但這道題還有坑點就是運算符的優先級:點比等號的優先級高。

也就是說,先執行的是

e.x = {n : 4}

 

而後執行:

e = { n: 4}

 

首先說先執行的代碼執行後的結果

e.x被賦值後,至關於堆內存中,對象{n:1}多了一個屬性x,而且值是{n : 4};

此時,e和f的值狀況分別是這樣的(注意下邊的obj1和obj2分別是我本身爲了區分對象給命名的)

 

而後,執行e = { n : 4}; 此時js引擎要作的事情就是:

開闢一個新的堆空間放新對象obj2 --> {n : 4}

而後將這個新對象的地址從新賦值給e。這一步能夠這麼理解:

var e = 'obj1的地址'; e = 'obj2的地址';

 

最終e在棧空間的值就是obj2的地址。

 

最後,執行console.log(f):

雖然e和obj1切斷了關係,可是f還和obj1有關係。因此f打印出來的就是整個obj1。

思考:e.x爲何是undefined而不報錯? 

這裏e被從新指向obj2後,obj2裏邊並無x值,因此e.x是不存在的。

正常狀況下,找不到一個變量會報錯: 變量 is not defined。可是爲何對象的屬性值不存在時只是輸出undefined呢?

這一點,咱們獲取e.x會去對象身上找,若是不存在x,會去對象的原型鏈上找,一直找到最頂端,都找不到時就返回「undefined」

 

3、引擎和做用域的對話

2018-12-05  21:14:46

觀察下邊的這段代碼,進行一次引擎和做用域的對話:

1 function foo(a){ 2   var b = a; 3   return a + b; 4 } 5 var c = foo(2);

找出裏邊的幾處RHS查詢、幾處LHS查詢?分別是哪裏?

 

 

答案:

 

4、垃圾回收機制 

背誦,具體文案見本週主題閱讀第四條

 垃圾回收器按期掃描對象,並計算引用了每一個對象的其餘對象的數量。若是一個對象的引用數量爲 0(沒有其餘對象引用過該對象),或對該對象的唯一引用是循環的,那麼該對象的內存便可回收。

 

 

5、從內存看,null和undefined的本質區別是什麼?

null也是js基本類型之一,它是一個字面量,特指對象的值未設置。表示缺乏的標識,指示變量未指向任何對象。

undefined也是js基本類型之一,是全局對象的一個屬性。聲明一個變量,未進行初始化時,這個變量的值就是undefiend,

給一個全局變量賦值null,至關於將這個變量的指針對象及值清空,若是是給對象的屬性賦值null,或者局部變量賦值爲null,至關於給這個屬性分配一塊空的內存空間,而後值是null。

js會回收全局變量爲null的對象。

 

6、ES6語法中,const聲明一個只讀的常亮,那爲何下面能夠修改const的值

const foo = {};

// 爲foo添加一個屬性,能夠成功
foo.prop = 123;
for.prop; // 獲得123

// 將foo指向另外一個對象,報錯:
foo = {};//TypeError: 'foo' is read-only

  

const實際上並非指變量的值不可改動,而是變量指向的那個內存地址所保存的數據不得改動。

對於簡單類型的數據(string、number、boolean、null、undefined),值就保存在變量指向的那個內存地址,所以等同於常量

但對於引用類型的數據(複合類型的數據:對象、數組),變量指向的內存地址,保存的是一個指向堆內存實際數據的指針,const只能保證這個指針

是固定的(即指向的地址不變)。至於這個地址對應的堆內存中數據結構的變化,他是控制不了的。

所以,在上例中,爲foo添加屬性,實際上操做的是堆內存中foo對象的數據結構,const管不着,

而改變foo的指針,指向另外一個對象,const是不答應的。

 

7、前端中內存泄露的幾種狀況:

(見本週閱讀主題篇第六條)

 

 

setTimeout 的第一個參數使用字符串而非函數的話,會引起內存泄漏。
閉包、控制檯日誌、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)

 

 

8、擴展題:淺拷貝和深拷貝

學了堆棧內存空間,應該就理解了什麼叫簡單數據類型存在棧內存,複雜數據類型存在堆內存了。

而後面試中,常常會問、業務中也常常會遇到的問題就是深淺拷貝的問題了。

棧內存中簡單數據類型直接拷貝就能獲得一個副本,可是複雜數據類型的拷貝若是也想獲得一個副本,就須要深拷貝了。

具體源碼見文章《js中的淺拷貝和深拷貝

相關文章
相關標籤/搜索