JS 內存管理機制及驗證

做用域

JavaScript 的變量被做用域所限制,若是超出了做用域,那麼變量就沒有辦法再被使用,這樣作的優勢是:前端

  • 能夠避免當前的變量轉爲全局變量
  • 有效限制變量的做用區域

而變量做用域也會按照聲明方式的不一樣,產生不一樣的做用域:程序員

  • 未聲明:全局變量
  • var 聲明:做用域在 函數
  • letconst 聲明:做用域在 {}

var 聲明的變量

在函數內聲明的變量其做用域會被限制在該函數的調用棧中,在外部沒法直接獲得該做用域內的變量。下面的例子中, fn 函數內的變量在全局下是沒有辦法查看的。面試

function fn() {
  var a = 1;
}
fn();
console.log(a); // 沒法獲得 fn 函數內的 a 變量

因此經常使用 "當即函數" 來限制變量的做用域,主要是避免全局變量的產生。segmentfault

(function() {
  var b = 1;
})();
console.log(b); // 沒法獲得 fn 函數內的 b 變量

let、const 聲明的變量

ES6 以後所新增的letconst 做用域則與過去不一樣,改用 {} 做爲做限制用域的方式,這讓 for 循環及部分語法避免產生多餘的變量來影響做用域。數組

var 不一樣的是 const 所定義的變量做用域被限制在 {} 中。因此這個例子中的變量 c 可在外部獲得值,d 則沒法取到。瀏覽器

{
  var c = 1;
  const d = 1;
}
console.log(c); // 1
console.log(d); // Uncaught ReferenceError: d is not defined,沒法取到變量 d

內存管理機制

每當咱們新增一個變量時,在內存中就會佔用一個位置來保存它的值,以便在程序後續運行時能夠屢次使用。服務器

下面的代碼會在內存中開闢一個 a 的空間來存儲數字 1 的值。微信

var a = 1

流程以下:多線程

  1. 開闢一個內存空間來存放變量 a ,不過這時尚未賦值(緣由可參考:Hoisting)

image.png

  1. a 賦值。

image.png

全部的變量都會佔用內存空間,除此以外對象、數組的屬性以及函數參數等也會用相同的概念進行佔用。調用一個函數時,每個函數的做用域也都會反覆的進行內存佔用。隨着應用程序愈來愈複雜的狀況下,若是持續的佔用內存而沒有進行適當的釋放,那麼內存可能會被耗盡。框架

JavaScript 引擎具備內存回收的機制,會釋放再也不使用的變量內存,其基本概念爲:當沒有任何的引用指向它時就會釋放內存。

來自MDN:collectible if there are zero references pointing to it.

內存釋放的驗證

下面用一個例子來講明及驗證內存釋放的機制,首先用一段函數來產生一個很是長的字符串,長字符串會佔用大量的內存空間。

在調用 randomString 函數後會返回一個很長的字串:

function randomString(length) {
  var result = '';
  var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  var charactersLength = characters.length;
  for (var i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}

案例一:使變量維持在可引用的狀態(不會釋放內存)

定義一個全局變量 demoData,這個變量會持續維持可被引用的狀態。

var demoData = []; // 全局變量
function getData() {
  for (let i = 0; i < 1000; i++) {
    demoData.push(randomString(5000))
  }
}
getData()
console.log(demoData); // 可引用 demoData 值

執行這段代碼後,進入 Chrome DevTools 中的 Memory 頁,這個功能能夠獲得當前頁面所佔用的內存情況。接下來點擊 "Take snapshot" 按鈕。

image.png

能夠看到目前執行完這段代碼後佔用了 6.2MB 的內存空間(注意:任何瀏覽器環境和插件都會影響所佔用的內存狀態)。

image.png

案例二:使變量沒法再次被引用(執行後釋放內存)

限制變量的做用域,使變量沒法再被外部引用。

此段代碼依然會執行這個函數,也會將值賦值給變量,但外部沒法再次引用 demoData 的值。

function getData() {
  var demoData = []; // 局部變量
  for (var i = 0; i < 1000; i++) {
    demoData.push(randomString(5000))
  }
}
getData();

而後回到 Memory 頁點擊 "Take snapshot" 從新取得內存的狀態,接下來會獲得與前面不一樣的結果,此次只佔用了 1.2MB 的內存(其中 5MB 被釋放掉了)

image.png

總結

經過前面的例子,咱們知道了做用域以及內存之間的關係,而內存管理也是前端打工人必需要掌握的知識(除了控制內存的使用大小,還需在必要時保留而不被釋放)。

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索