2018-12-03 20:57:24javascript
內存泄漏指任何對象在您再也不擁有或須要他以後,其仍然存在內存中。html
也就是程序中分配的堆內存空間沒有被及時釋放或沒法釋放,致使的內存佔用過多,形成程序運行速度減慢甚至卡頓、崩潰。前端
在瀏覽器中識別內存泄漏的方法:java
一段」高清「gif展現給你:面試
詳見:【進階1-4期】JavaScript深刻之帶你走進內存機制數組
一共給本身出了三道題:瀏覽器
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引擎要作的事情以下:
以後的代碼,運行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被從新指向obj2後,obj2裏邊並無x值,因此e.x是不存在的。
正常狀況下,找不到一個變量會報錯: 變量 is not defined。可是爲何對象的屬性值不存在時只是輸出undefined呢?
這一點,咱們獲取e.x會去對象身上找,若是不存在x,會去對象的原型鏈上找,一直找到最頂端,都找不到時就返回「undefined」
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查詢?分別是哪裏?
答案:
背誦,具體文案見本週主題閱讀第四條
垃圾回收器按期掃描對象,並計算引用了每一個對象的其餘對象的數量。若是一個對象的引用數量爲 0(沒有其餘對象引用過該對象),或對該對象的唯一引用是循環的,那麼該對象的內存便可回收。
null也是js基本類型之一,它是一個字面量,特指對象的值未設置。表示缺乏的標識,指示變量未指向任何對象。
undefined也是js基本類型之一,是全局對象的一個屬性。聲明一個變量,未進行初始化時,這個變量的值就是undefiend,
給一個全局變量賦值null,至關於將這個變量的指針對象及值清空,若是是給對象的屬性賦值null,或者局部變量賦值爲null,至關於給這個屬性分配一塊空的內存空間,而後值是null。
js會回收全局變量爲null的對象。
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是不答應的。
(見本週閱讀主題篇第六條)
setTimeout 的第一個參數使用字符串而非函數的話,會引起內存泄漏。
閉包、控制檯日誌、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)
學了堆棧內存空間,應該就理解了什麼叫簡單數據類型存在棧內存,複雜數據類型存在堆內存了。
而後面試中,常常會問、業務中也常常會遇到的問題就是深淺拷貝的問題了。
棧內存中簡單數據類型直接拷貝就能獲得一個副本,可是複雜數據類型的拷貝若是也想獲得一個副本,就須要深拷貝了。
具體源碼見文章《js中的淺拷貝和深拷貝》