JavaScript中的內存釋放

zwba0830 7年前發布 | 10K 次閱讀 JavaScript開發 JavaScript

一、如何查找上級作用域

在JavaScript中的預解析 ,有講到作用域鏈的概念,本次在正式講JavaScript中的內存釋放之前,先看一個問題: 如何查找當前作用域的上級作用域

var num = 20;
function fn() {
    var num = 200;
    return function () {
        console.log(num);
    };
}
var f = fn();
f(); // 輸出 200

以上代碼fn中返回了一個函數,用f去接收這個返回的函數,然后再執行f(),最后輸出的是200,剛接觸的同學可能會有疑問,為什么在全局作用域下執行的f(),為什么輸出的num不是全局作用域中的20,而是fn函數的私有作用域中的200!

上級作用域查找規則:看當前函數是在哪個作用域下定義的,那么它的上級作用域就是誰,和函數哪里執行沒有任何的關系。

以上的代碼在全局的變量和函數進行預解析之后,執行fn函數,fn函數又進行預解析形成自己的私有作用域,然后執行fn函數中的代碼,最后返回一個函數,該函數被f接收。當f執行的時候,有一行代碼輸出num: console.log(num) ,根據作用域搜索規則,首先在自己的作用域中找,沒有找到,然后再到上級的作用域中查找,根據作用域查找的規則, 只看當前函數在哪個作用域下定義的 ,所以f函數的上級作用域是fn。

如何查找上級作用域.png

有了以上的基礎之后,再看:

var num = 20;
function fn() {
    var num = 200;
    return function () {
        console.log(num);
    };
}
var f = fn();
f(); // 輸出 200

~function () { var num = 2000; f(); // 輸出什么呢? }();</code></pre>

加上了一個自執行函數,我們知道自執行函數是有自己的作用域的,但是此時f函數執行,依然輸出200。 要時刻上級作用域的查找規則: 只看當前函數在哪個作用域下定義的

這時候很多同學可能會疑惑了,我們不是講解JavaScript的內存釋放的嘛?怎么還講起了作用域的內容,稍安勿躁...在JavaScript的內存釋放中要用到這些知識呢!

二、堆內存的釋放

對象數據類型或者函數類型在定義的時候,首先都會開辟一個堆內存,堆內存有一個引用地址,如果外面有引用這個地址,我們就說這個內存被占用了,就不能銷毀了。

var obj1 = {name:"iceamn"};
var obj2 = obj1;

堆內存.png

如果想要讓堆內存釋放(銷毀),只需要把所有引用它的變量賦值為null即可,如果當前的堆內存沒有任何東西被占用了,那么瀏覽器會在空閑的時候把它銷毀。也就是說,在上面的那種感覺情況下,只有把obj1和同obj2都置為null之后,0xff11這塊對堆內存才會被釋放,只要還有變量引用0xff11這塊內存,它就不會釋放。

三、棧內存的釋放

3.1、全局作用域

在全局作用域下,只有當頁面關閉的時候,全局作用域才會被銷毀。

3.2、私有作用域

一般情況下,函數執行會形成一個新的私有作用域(在ES6之前只有函數執行才會產生私有作用域),當私有作用域中的代碼執行完成后,當前作用域都會主動的進行釋放和銷毀。

不過依然有特殊的情況存在:當前私有作用域中的部分內容被作用域以外的東西占用了,那么當前作用域就不能銷毀了。

3.2.1、 函數返回來一個引用數據類型的值(數組、函數...),并且該引用類型的值在函數的外面被一個其他變量接收了,這種情況下形成的私有作用域都不會銷毀。

注意兩個條件:

(1)函數返回引用數據類型的值;

(2)該引用類型的值在函數外面被一個其他變量接收了;

function fn() {
    var num = 100;
    return function () {
        num ++;
        console.log(num);
    }
}
var f = fn(); // fn執行形成的作用域就不能再銷毀了

注意:即使fn返回的函數中什么代碼都沒有,沒有使用到fn私有作用域中的任何變量和函數,在以上情況下,fn的私有作用域也不會被銷毀,即:

function fn() {
    var num = 100;
    return function () {
    }
}
var f = fn();

3.2.2、 在一個私有作用域中,給DOM元素綁定方法,私有作用域不能被銷毀

var btn = document.getElementById('btn1');
~function () {
    btn.onclick = function () {
    }
}();

在自執行函數中形成了一個私有的作用域,在這個私有作用域中為頁面上的一個button元素綁定了點擊事件,所以這個私有作用域也不能被銷毀。

自執行函數的情況.png

3.2.3、 “不立即銷毀”

function fn() {
    var num = 100;
    return function () {
    }
}
fn()(); // 首先執行fn,返回一個小函數對應的內存地址,然后緊接著讓返回的小函數再執行

以上代碼就是“不立即銷毀”的情況,fn返回的函數沒有被其他的任何變量占用,但是還需要執行一次,所以暫時不能銷毀,但返回的值執行完成后,瀏覽器會在空閑的時候把它銷毀了。

還記得一開始介紹的上級作用域嗎,我們再對那張圖進行分析:

上級作用域的情況.png

只要某作用域還有被引用,那么該作用域就不能被銷毀,一旦沒有任何變量引用了,該私有作用域就會被銷毀了。

四、練習題

  • 第一題

    function fn() {
      var i = 10;
      return function (n) {
          console.log(n + (++i));
      };
    }
    var f = fn();
    f(10); // 21
    f(20); // 32
    fn()(10); // 21
    fn()(20); // 31
  • 第二題

    function fn(i) {
          return function (n) {
              console.log(n + i++);
          }
      }
      var f = fn(13);
      f(12);//->25
      f(14);//->28
      fn(15)(12);//->27
      fn(16)(13);//->29

 

來自:http://www.jianshu.com/p/3b7946c4b118

 

 本文由用戶 zwba0830 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!