一入前端深似海,從此紅塵是路人系列第三彈之淺析JavaScript閉包

李一花發表於2018-07-19

前言:最近由於公司專案太忙,很久沒有更新部落格了,加上之前就一直說要發表一篇有關閉包的部落格幫助小夥伴們好好的理解一些JavaScript中的難點。所以,今天趁著國慶假期前趕緊寫了去,寫完國慶好好出去浪個夠。

首先,必須要提的就是閉包它絕對算的上是JavaScript中的一大難點,當然也是一大重點。N多高階程式都需要或者必須用到閉包才能得以實現。參考了N篇很牛叉的對於閉包理解的文章,接下來我將陳述一下鄙人自己對於閉包的理解,希望可以幫助小夥伴們通俗切入閉包這個點。

1、變數作用域
理解閉包很重要的一點就是需要先理解JavaScript特殊的變數作用域。
而變數作用域無非兩種形式,全域性變數和區域性變數。而在JavaScript中,所有的函式它都可以在其內部訪問到全域性變數。

var n = 123;
function test1(){
  alert(n);
}
test1(); //123

而在函式外部也無法讀取函式內的區域性變數。

function test1(){
  var n=123;
}
test1();
alert(n); // Uncaught ReferenceError: n is not defined

當然在定義變數的時候,一定要記住加上var關鍵字,不然,JavaScript會預設你定義了一個全域性變數。

function test1(){
  n=123;
}
test1();
alert(n); // 123

2、函式外部訪問區域性變數
很多時候我們需要在一個函式中去訪問另外一個函式內部的區域性變數,可是上面又說了一個函式內部的區域性變數是不允許被其他函式訪問的。怎麼辦呢?
那我們就需要在函式內部在定義一個函式,這樣就可以在其內部函式中訪問到它內部的區域性變數了。上程式碼理解。

function test1(){
  // 函式test2就被包括在函式test1內部,這時test1內部的所有區域性變數,對test2都是可見的
 var n=123;
 function test2(){
    // test2內部的區域性變數則不能被test1訪問
    // 這就是Javascript語言特有的"鏈式作用域"結構(chain scope),子物件會一級一級地向上尋找所有父物件的變數。所以,父物件的所有變數,對子物件都是可見的,反之則不成立。
    var n1 = 321;
  alert(n); 
 }
  //alert(n1);  // error
 return test2;
}
var result=test1();
result(); // 123

而上面程式碼中的test2就是一個閉包,它是一種能夠讀取其他函式內部變數的函式,或者直接理解為定義在一個函式內部的函式。

3、閉包的用途
閉包可以用在很多地方。但它最大的用處有兩點。第一點是以上提到的,訪問其他函式內部的區域性變數。還有一個很重要的用途則是讓這些變數始終儲存在記憶體中。不多解釋,直接看程式碼理解

function test1(){
  /**
   * test1是test2的父函式,而test2被賦給了一個全域性變數result,這導致test2始終在記憶體中
   * 而test2的存在依賴於test1,因此test1也始終在記憶體中
   * 這樣test1中的區域性變數就不會在呼叫結束後,被垃圾回收機制回收。
   */
 var n=123;
  // nAdd前面沒有var關鍵字,它是一個全域性變數。nAdd的值是一個匿名函式,而這個匿名函式本身也是一個閉包
  // 它相當於一個setter,可以在函式外部對函式內部的區域性變數進行操作。
 nAdd = function(){
    n+=1
  }
 function test2(){
  alert(n);
 }
 return test2;
}
var result=test1();
result(); // 123
nAdd();
result(); // 124

4、閉包使用中需注意的問題

1)由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體洩露。解決方法是,在退出函式之前,將不使用的區域性變數全部刪除。

2)閉包會在父函式外部,改變父函式內部變數的值。所以,如果你把父函式當作物件使用,把閉包當作它的公用方法,把內部變數當作它的私有屬性,這時一定要小心,不要隨便改變父函式內部變數的值。

5、練習思考題

當你可以理解以下兩段程式碼執行結果時,那麼恭喜你,你已經理解了閉包的執行機制了。

//程式碼一
var name = "The Window";
var object = {
 name : "My Object",
 getNameFunc : function(){
  return function(){
   return this.name;
  };
 }
};
alert(object.getNameFunc()());
//程式碼二
var name = "The Window";
var object = {
 name : "My Object",
 getNameFunc : function(){
  var that = this;
  return function(){
   return that.name;
  };
 }
};
alert(object.getNameFunc()());

好了,文章最後。還是那句話,如果覺著可以幫助到小夥伴的話,求點個贊哦。若覺著哪裡有說錯或者寫錯的地方,還請小夥伴們輕噴,但是歡迎小夥伴隨時指正部落格中的錯誤然後大家一起交流探討!(*^__^*)

文釋出時間為:2016年09月30日
原文作者:qiangdada 

本文來源:開源中國 如需轉載請聯絡原作者


相關文章