【初窺javascript奧秘之閉包】葉大俠病都好了,求不踩了:)

pswyjz發表於2021-09-09

前言

咳咳,葉大俠病了,昨天晚上回家時候在車上就不舒服,果然回來就掛了,本來還想今天接著上班撐下去的。但是昨天又看到一個IT鉅子掛了,所以果斷請了一個假!!!

但是早上7.00左右就迷迷糊糊的醒了,於是我在想我是不是該“身殘志堅”一番。。。。

咳咳,以上玩笑,我們最近一起學習了很多CSS的東西,相信大家的CSS水平必定提高了吧???所以我們接下來一段時間來看看javascript吧,今天我們一起來看看閉包這個傢伙!

本文參考:

http://www.cnblogs.com/TomXu/

http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html

http://www.cnblogs.com/frankfang/archive/2011/08/03/2125663.html

http://www.cnblogs.com/xiaotie/archive/2011/08/03/2126145.html

執行上下文(execution context)

又是上下文。。。無論是曾經學習.net時候的httpcontext,還是最近學習的block formatting context,都是上下文!

我們可以感覺到上下文的存在,就如感覺得到空氣一樣,我們卻將它拿不出來,於是我們來看看執行上下文吧。

每次當控制器轉到ecmascript可執行程式碼的時候,就會進入一個執行上下文。活動的執行上下文組在邏輯上為一個堆疊,堆疊最底部一定是全域性上下文(global context),頂部便是當前上下文

這個句話是很有玄機的哦,比如我們在一個函式環境中,我們首先用的是當前變數,若是沒有就在父級函式中找,最後就在window中找。

PS:我們在寫部落格時候,中文沒有問題,突然打一個字母上,你會發現整行都下移了1/2px,各位知道神馬原因麼???:)

eval在執行時候會產生一個呼叫上下文的東東(calling context),所以eval中定義的變數會影響函式環境中的變數。

作用域

閉包有作用域鏈有很多說不清道不明的關係,所以我們還是先看看作用域吧。

任何語言都會有作用域的概念,作用域便是變數與函式可訪問範圍,簡稱變數與函式的生命週期。javascript中分為全域性與區域性兩種,直白點window與function

 

圖片描述

var name = '葉小釵';function func() {    var id = '刀狂劍痴';    function alertId() {        alert(id);    }    alertId();}alert(name);//葉小釵alert(id);//錯誤func(); //刀狂劍痴alertId();//錯誤

圖片描述

此處name屬於window屬性便是全域性的(有些瀏覽器自帶name屬性小心陷阱!)

func也是全域性的,但是func這個作用域鏈包含了全域性的,所以他可以訪問外部的,但是外部不能訪問進來。

鏈子。。。

在js中什麼都是物件,函式和其它物件一樣擁有很多屬性,其中一個比較特殊的屬性是:

[[scope]],scope翻譯過來就是作用域,該屬性包含了函式被建立的作用域中的物件的集合:

於是傳說中的作用域鏈誕生了,他決定了哪些資料能被哪些函式訪問。

當一個函式建立後,他的作用域鏈會被建立此函式的作用域中可訪問的資料物件填充:

function add (a, b) {    var sum = a + b;    return sum;}

在函式add建立時,他的作用域鏈中會填入一個全域性物件,我們可以想象為window被壓入了其[[scope]]

函式add的作用域在呼叫時會被用到,此時會建立一個“執行時上下文(execution context)”的內部物件,他定義了函式執行時的環境,每個執行時上下文都有自己的作用域鏈,用於識別符號解析,當execution context建立時,而他的作用域鏈初始化為當前執行函式的[[scope]]所包含的物件。

所有相關的東東會按照其在函式中出現的順序被複制到執行時上下文的作用域鏈中,他們共同組成了“活動物件(activation object)”,該物件就包含了函式的所有區域性變數,引數,以及this,然後會排在我們作用域堆疊的頂端,我們可以最先獲取,在執行時上下文結束(函式執行完畢)後,GC便會回收其空間。

在函式執行過程中,遇到一個變數便會經歷一次識別符號解析以決定從哪裡獲取以及儲存資料。

這個過程首先從我們的“堆疊”頂端開始搜尋,也就是活動物件的開始,若是有該變數便獲取值,

若是沒有則向下,知道window為止,沒有就報錯!

記憶體溢位

是想我們的瀏覽器記憶體若是搞滿了,自然會瀏覽器崩潰,這是我們閉包之中需要考慮到的東西,因為他會導致我們一些東西無法GC。

閉包,神奇的魔法

閉包是神奇的魔法,因為他幹了很多“壞事”,達到了出人意料的結果。

所謂閉包,便是function巢狀function,內部function可以訪問外部的變數。若是外部函式返回了內部函式,那麼就閉了一個包

圖片描述

 1 function a() { 2     var name = '葉小釵'; 3     function alertName() { 4         alert(name); 5     } 6     return alertName; 7 } 8  9 var func = a();10 func();//葉小釵

圖片描述

這個傢伙是個壞孩子,怎麼說呢,壞孩子招人疼啊!

我們看第9行,按我們之前的理解a執行結束後,整個活動物件是不是該被銷燬?是不是與a有關的東西都該GC呢?

確實是這樣的,但是我們這裡就產生了一個閉包,阻止了其銷燬,因為他裡面的變數被用到了。

這裡的一個事實便是外部的func訪問到了a內部的變數!

一個經典的例子:

圖片描述

 1 function outer() { 2     var o = {}; 3     for (var i = 0; i 

圖片描述

我們來看這個,大家應該都比較熟悉了,我們知道他全部會列印10。

這是因為我們將outer給予funcs時候,outer的任務就完成啦,他現在內部的i便是10,我們現在再來呼叫函式,所有的i自然是指向一個[[scope]],所以變數i就是10!!!

要怎麼解決大家也一目瞭然:

圖片描述

 1 function outer() { 2     var o = {}; 3     for (var i = 0; i 

圖片描述

相當於每次i是以一個副本的方式重新複製了一番,這個是可以達到我們的目的,但是有時候會對效能有一點影響,我們還是需要注意。

我們這裡就不扯遠了,在扯遠的話我估計也露餡了。。。呵呵,最後我們來說一點應用吧。

閉包的應用

jquery時如何神奇的使用閉包的我現在還看不透,那是我1年後的任務,我這不關注他。

我這裡說的應用時原來專案過程中遇到的,所以沒有程式碼,我這邊說下問題吧:

我們點選一個按鈕,會向伺服器發一個ajax請求,其中包含一些data,然後資料返回回來後我們又會用到開始傳過去的一些data中的敏感資訊,比如id。

這個時候一種比較笨的方法就是伺服器給我們傳回來,但是閉包出現後,我們便可以直接在函式中定義函式,而使用我們之前的data即可。

該例子有段時間了,大家看不清楚也沒有關係啦。。。。

結語

葉大俠病了,求安慰,求按頂。。。。。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/755/viewspace-2806592/,如需轉載,請註明出處,否則將追究法律責任。

相關文章