欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Javascript閉包實例詳解

 更新時間:2015年11月29日 10:48:08   作者:Darren Ji  
閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會繼續(xù)存在,本文通過代碼實例給大家介紹javascript閉包,對javascipt閉包相關知識感興趣的朋友一起學習吧

什么是閉包

閉包是什么?閉包是Closure,這是靜態(tài)語言所不具有的一個新特性。但是閉包也不是什么復雜到不可理解的東西,簡而言之,閉包就是:

閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會繼續(xù)存在。

閉包就是就是函數(shù)的“堆?!痹诤瘮?shù)返回后并不釋放,我們也可以理解為這些函數(shù)堆棧并不在棧上分配而是在堆上分配當在一個函數(shù)內(nèi)定義另外一個函數(shù)就會產(chǎn)生閉包上面的第二定義是第一個補充說明,抽取第一個定義的主謂賓——閉包是函數(shù)的‘局部變量'集合。只是這個局部變量是可以在函數(shù)返回后被訪問。(這個不是官方定義,但是這個定義應該更有利于你理解閉包)

理解Javascript的閉包非常關鍵,本篇試圖用最簡單的例子理解此概念。

function greet(sth){
  return function(name){
    console.log(sth + ' ' + name);
  }
}
//hi darren
greet('hi')('darren');

或者可以寫成這樣:

var sayHi = greet('hi');
sayHi('darren');


我們要提的問題是:為什么greet的內(nèi)部函數(shù)能使用sth這個變量?

其內(nèi)部大致運作如下:

→ 創(chuàng)建全局上下文
→ 執(zhí)行var sayHi = greet('hi');語句,創(chuàng)建greet上下文,變量sth存儲在greet上下文中。
→ 繼續(xù)執(zhí)行greet函數(shù)內(nèi)的語句,返回一個匿名函數(shù),雖然greet上下文從堆棧上消失,但sth變量依舊存在于內(nèi)存的某個空間。
→ 繼續(xù)執(zhí)行sayHi('darren');創(chuàng)建了sayHi上下文,并試圖搜尋sth變量,但在sayHi這個上下文中沒有sth變量。sayHi上下文會沿著一個作用域鏈找到sth變量對應的那個內(nèi)存。 外層函數(shù)就像一個閉包,其內(nèi)部函數(shù)可以使用外部函數(shù)的變量。

一個閉包的簡單例子

function buildFunctions(){
  var funcArr = [];
  for(var i = 0; i < 3; i++){
    funcArr.push(function(){console.log(i)});
  }
  return funcArr;
}
var fs = buildFunctions();
fs[0](); //3
fs[1](); //3
fs[2](); //3

以上,為什么結果不是0, 1, 2呢?

--因為i作為一個閉包變量,當前值為3,被內(nèi)部函數(shù)使用。要實現(xiàn)想要的效果,可以在遍歷的時候每一次遍歷創(chuàng)建一個獨立的上下文使其不受閉包影響。而自觸發(fā)函數(shù)可以實現(xiàn)獨立上下文。

function buildFunctions(){
  var funcArr = [];

  for(var i = 0; i < 3; i++){
    funcArr.push((function(j){
      return function(){
       console.log(j);
      };
    }(i)));
  }

  return funcArr;
}
var fs = buildFunctions();
fs[0](); //0
fs[1](); //1
fs[2](); //2

本篇的兩個例子正好體現(xiàn)了閉包的2個方面:一個是內(nèi)部函數(shù)使用閉包變量,另一個是把內(nèi)部函數(shù)寫在自觸發(fā)函數(shù)中從而避免受閉包影響。

做為局部變量都可以被函數(shù)內(nèi)的代碼訪問,這個和靜態(tài)語言是沒有差別。閉包的差別在于局部變變量可以在函數(shù)執(zhí)行結束后仍然被函數(shù)外的代碼訪問。這意味 著函數(shù)必須返回一個指向閉包的“引用”,或?qū)⑦@個”引用”賦值給某個外部變量,才能保證閉包中局部變量被外部代碼訪問。當然包含這個引用的實體應該是一個 對象,因為在Javascript中除了基本類型剩下的就都是對象了。可惜的是,ECMAScript并沒有提供相關的成員和方法來訪問閉包中的局部變 量。但是在ECMAScript中,函數(shù)對象中定義的內(nèi)部函數(shù)() inner function是可以直接訪問外部函數(shù)的局部變量,通過這種機制,我們就可以以如下的方式完成對閉包的訪問了。

function greeting(name) {
   var text = 'Hello ' + name; // local variable
   // 每次調(diào)用時,產(chǎn)生閉包,并返回內(nèi)部函數(shù)對象給調(diào)用者
   return function () { alert(text); }
}
var sayHello=greeting( "Closure" );
sayHello() // 通過閉包訪問到了局部變量text

上述代碼的執(zhí)行結果是:Hello Closure,因為sayHello()函數(shù)在greeting函數(shù)執(zhí)行完畢后,仍然可以訪問到了定義在其之內(nèi)的局部變量text。

好了,這個就是傳說中閉包的效果,閉包在Javascript中有多種應用場景和模式,比如Singleton,Power Constructor等這些Javascript模式都離不開對閉包的使用。

ECMAScript閉包模型

ECMAScript到底是如何實現(xiàn)閉包的呢?想深入了解的親們可以獲取ECMAScript 規(guī)范進行研究,我這里也只做一個簡單的講解,內(nèi)容也是來自于網(wǎng)絡。

在ECMAscript的腳本的函數(shù)運行時,每個函數(shù)關聯(lián)都有一個執(zhí)行上下文場景(Execution Context) ,這個執(zhí)行上下文場景中包含三個部分

文法環(huán)境(The LexicalEnvironment)
變量環(huán)境(The VariableEnvironment)
this綁定

其中第三點this綁定與閉包無關,不在本文中討論。文法環(huán)境中用于解析函數(shù)執(zhí)行過程使用到的變量標識符。我們可以將文法環(huán)境想象成一個對象,該對 象包含了兩個重要組件,環(huán)境記錄(Enviroment Recode),和外部引用(指針)。環(huán)境記錄包含包含了函數(shù)內(nèi)部聲明的局部變量和參數(shù)變量,外部引用指向了外部函數(shù)對象的上下文執(zhí)行場景。全局的上下文 場景中此引用值為NULL。這樣的數(shù)據(jù)結構就構成了一個單向的鏈表,每個引用都指向外層的上下文場景。

例如上面我們例子的閉包模型應該是這樣,sayHello函數(shù)在最下層,上層是函數(shù)greeting,最外層是全局場景。如下圖:

 

因此當sayHello被調(diào)用的時候,sayHello會通過上下文場景找到局部變量text的值,因此在屏幕的對話框中顯示出”Hello Closure”
變量環(huán)境(The VariableEnvironment)和文法環(huán)境的作用基本相似,具體的區(qū)別請參看ECMAScript的規(guī)范文檔。

閉包的樣列

前面的我大致了解了Javascript閉包是什么,閉包在Javascript是怎么實現(xiàn)的。下面我們通過針對一些例子來幫助大家更加深入的理解閉包,下面共有5個樣例,例子來自于JavaScript Closures For Dummies(鏡像)。

例子1:閉包中局部變量是引用而非拷貝

function say667() {
  // Local variable that ends up within closure
  var num = 666;
  var sayAlert = function() { alert(num); }
  num++;
  return sayAlert;
}
var sayAlert = say667();
sayAlert()

因此執(zhí)行結果應該彈出的667而非666。

例子2:多個函數(shù)綁定同一個閉包,因為他們定義在同一個函數(shù)內(nèi)。

function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 666;
  // Store some references to functions as global variables
  gAlertNumber = function() { alert(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}
setupSomeGolbals(); // 為三個全局變量賦值
gAlertNumber(); //666
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(12);//
gAlertNumber();//12

例子3:當在一個循環(huán)中賦值函數(shù)時,這些函數(shù)將綁定同樣的閉包

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    result.push( function() {alert(item + ' ' + list[i])} );
  }
  return result;
}
function testList() {
  var fnlist = buildList([1,2,3]);
  // using j only to help prevent confusion - could use i
  for (var j = 0; j < fnlist.length; j++) {
    fnlist[j]();
  }
}

testList的執(zhí)行結果是彈出item3 undefined窗口三次,因為這三個函數(shù)綁定了同一個閉包,而且item的值為最后計算的結果,但是當i跳出循環(huán)時i值為4,所以list[4]的結果為undefined.

例子4:外部函數(shù)所有局部變量都在閉包內(nèi),即使這個變量聲明在內(nèi)部函數(shù)定義之后。

function sayAlice() {
  var sayAlert = function() { alert(alice); }
  // Local variable that ends up within closure
  var alice = 'Hello Alice';
  return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

執(zhí)行結果是彈出”Hello Alice”的窗口。即使局部變量聲明在函數(shù)sayAlert之后,局部變量仍然可以被訪問到。

例子5:每次函數(shù)調(diào)用的時候創(chuàng)建一個新的閉包

function newClosure(someNum, someRef) {
  // Local variables that end up within closure
  var num = someNum;
  var anArray = [1,2,3];
  var ref = someRef;
  return function(x) {
    num += x;
    anArray.push(num);
    alert('num: ' + num +
    '\nanArray ' + anArray.toString() +
    '\nref.someVar ' + ref.someVar);
  }
}
closure1=newClosure(40,{someVar:'closure 1'});
closure2=newClosure(1000,{someVar:'closure 2'});
closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'

閉包的應用

Singleton 單件:

var singleton = function () {
  var privateVariable;
  function privateFunction(x) {
    ...privateVariable...
  }
  return {
    firstMethod: function (a, b) {
      ...privateVariable...
    },
    secondMethod: function (c) {
      ...privateFunction()...
    }
  };
}();

這個單件通過閉包來實現(xiàn)。通過閉包完成了私有的成員和方法的封裝。匿名主函數(shù)返回一個對象。對象包含了兩個方法,方法1可以方法私有變量,方法2訪 問內(nèi)部私有函數(shù)。需要注意的地方是匿名主函數(shù)結束的地方的'()',如果沒有這個'()'就不能產(chǎn)生單件。因為匿名函數(shù)只能返回了唯一的對象,而且不能被 其他地方調(diào)用。這個就是利用閉包產(chǎn)生單件的方法。

相關文章

最新評論