亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

搜索

JavaScript閉包

原創(chuàng) 2016-11-11 13:21:40 503
摘要:前言每個函數都有自己的執(zhí)行環(huán)境。當某個函數被調用時,會創(chuàng)建一個執(zhí)行環(huán)境(execution context)及相應的作用域鏈,并把作用域鏈賦值給一個特殊的內部屬性(即[[Scope]])。然后使用this、arguments和函數參數、內部變量、內部函數引用來初始化函數的活動對象(activation object)。作用域鏈(堆棧)是指向活動對象的指針列表,該函數的活動對象在棧頂,全局變量對象在

前言

每個函數都有自己的執(zhí)行環(huán)境。當某個函數被調用時,會創(chuàng)建一個執(zhí)行環(huán)境(execution context)及相應的作用域鏈,并把作用域鏈賦值給一個特殊的內部屬性(即[[Scope]])。然后使用this、arguments和函數參數、內部變量、內部函數引用來初始化函數的活動對象(activation object)。作用域鏈(堆棧)是指向活動對象的指針列表,該函數的活動對象在棧頂,全局變量對象在棧底。

PS:在 JavaScript 的執(zhí)行中會一直存在一個Execute Context Stack , 最下面一個是Global Context,創(chuàng)建的execution context會被壓入這個棧。

這里面提到幾個關鍵字:

1. 執(zhí)行環(huán)境(execution context)

聲明該函數有權訪問的變量和函數。

2. 作用域鏈(scope chain)

作用域鏈的創(chuàng)建規(guī)則是復制上一層環(huán)境的作用域鏈,并將指向本環(huán)境變量對象的指針放到鏈首。本質上是一個指向變量對象的指針列表,它只是引用,實際上不包含變量對象。另外JavaScript是函數作用域的,并沒有像Java、C那樣有塊級作用域。

PS:JS主要是詞法作用域(lexical scope,也即是靜態(tài)作用域),在詞法解析階段既確定了。但是有兩個特例,就是eval和with可以構成動態(tài)作用域。

3. 活動對象(activation object)

保存該函數arguments和函數參數、內部變量和內部函數引用。

例子:

function compare(value1, value2){
     if(value1 < value2){
          return -1;
     } else if(value1 > value2){
          return 1;
     } else{
          return 0;
     }
}
var result = compare(5, 10);

相應的執(zhí)行環(huán)境圖示:

555379-20160507152756156-306963152.png

閉包概念

JavaScript高級程序設計里面對閉包的描述是,“閉包是指有權訪問另一個函數作用域中的變量的函數”。我覺得嚴格上講,這個“另一個”是閉包函數的外部函數。

ECMAScript中,閉包指的是:

1. 從理論角度:所有的函數。因為它們都在執(zhí)行的時候就將上層上下文的數據保存起來了(體現在作用域鏈)。哪怕是簡單的全局變量也是如此,因為函數中訪問全局變量就相當于是在訪問自由變量,這個時候使用最外層的作用域。

2. 從實踐角度:以下函數才算是閉包:

(1). 即使創(chuàng)建它的上下文已經銷毀,它仍然存在(比如,內部函數從父函數中返回)。

(2). 在代碼中引用了自由變量。

PS:自由變量是指在函數中使用的,但既不是函數參數也不是函數的局部變量的變量。

閉包使用場景

1. 循環(huán)遍歷中的延遲使用變量

我們可能在循環(huán)中為某些元素注冊事件,或setTimeout執(zhí)行一些代碼,這段代碼使用到循環(huán)部分的變量(我稱這個變量為延遲使用變量),可能需要使用閉包保證變量的準確。

例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript">
          function onMyLoad(){
            var arr = document.getElementsByTagName("p");
            //有問題寫法
            for(var i = 0; i < arr.length;i++){
                arr[i].onclick = function(){
                    alert(i);
                }
            }
            //閉包寫法
            for(var i = 0;i<arr.length;i++){
               (function (arg) {
                  arr[i].onclick = function () {
                     alert(arg);
                  }
               })(i);
            }
        }
    </script></head><body onload="onMyLoad()">
    <p>產品一</p>
    <p>產品二</p>
    <p>產品三</p>
    <p>產品四</p>
    <p>產品五</p>
</body>
</html>

2. 私有變量

JavaScript沒有私有變量這東西,只是用閉包模擬而已。

例子:

function MyObject(){
     var privateVar = 10;
     this.getPrivateVar = function(){
          return privateVar;
     }
}

3. 模塊模式

為單例創(chuàng)建私有變量和特權方法(有權訪問私有變量和私有函數的公有方法)就是模塊模式。

例子:

var singleton = (function(){
     var privateVar = 10;
     var getPrivateVar = function(){
          return privateVar;
     }
     return {
         getPrivateVar : getPrivateVar
     }
})();

4. 塊級作用域

可以用來模擬塊級作用域。

閉包注意地方

1. 閉包會使函數中的變量常駐內存,所以如果濫用閉包,會導致內存回收不回來,影響腳本性能。

例子:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };
  this.getMessage = function() {
    return this.message;
  };
}

上面的代碼并未利用到閉包的益處,因此,應該修改為如下常規(guī)形式:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

PS:前者每次實例化時候,每個對象里面都存在getName 、getMessage方法,后者則不會。

Q&A

1. 閉包是函數還是一種代碼行為?所有函數都是閉包嗎?

參考閉包概念

2. 變量查找順序?

當在函數中訪問一個變量的時候,搜索順序是先搜索自身的活動對象,如果存在則返回,如果不存在將繼續(xù)搜索作用域鏈上的活動對象,依次查找,直到找到為止。如果整個作用域鏈上都無法找到,則返回undefined。如果函數存在prototype原型對象,則在查找完自身的活動對象后,再查找自身的原型對象,再繼續(xù)查找。


發(fā)布手記

熱門詞條