ES6 之前 JS 沒有塊級作用域。例如
成都創(chuàng)新互聯(lián)提供網(wǎng)站設(shè)計、成都做網(wǎng)站、網(wǎng)頁設(shè)計,品牌網(wǎng)站建設(shè),1元廣告等致力于企業(yè)網(wǎng)站建設(shè)與公司網(wǎng)站制作,十余年的網(wǎng)站開發(fā)和建站經(jīng)驗,助力企業(yè)信息化建設(shè),成功案例突破數(shù)千家,是您實現(xiàn)網(wǎng)站建設(shè)的好選擇.
從上面的例子可以體會到作用域的概念,作用域就是一個獨立的 地盤 ,讓變量不會外泄、暴露出去。上面的name就被暴露出去了,因此, JS 沒有塊級作用域,只有全局作用域和函數(shù)作用域 。
全局作用域就是最外層的作用域 ,如果我們寫了很多行 JS 代碼,變量定義都沒有用函數(shù)包括,那么它們就全部都在全局作用域中。這樣的壞處就是很容易撞車、沖突。
這就是為何 jQuery、Zepto 等庫的源碼,所有的代碼都會放在 (function(){....})() 中。因為放在里面的所有變量,都 不會被外泄和暴露 ,不會污染到外面,不會對其他的庫或者 JS 腳本造成影響。這是函數(shù)作用域的一個體現(xiàn)。
附:ES6 中開始加入了塊級作用域,使用let定義變量即可,如下:
首先認(rèn)識一下什么叫做 自由變量 。如下代碼中,console.log(a)要得到a變量,但是在當(dāng)前的作用域中沒有定義a(可對比一下b)。當(dāng)前作用域沒有定義的變量,就稱為 自由變量 。自由變量如何得到 —— 向 父級 作用域?qū)ふ摇?/p>
如果父級也沒呢? 再一層一層向上尋找,直到找到全局作用域還是沒找到,就宣布放棄 。這種一層一層的關(guān)系,就是 作用域鏈 。
通過例子來理解閉包。
自由變量將從作用域鏈中去尋找,但是 依據(jù)的是函數(shù)定義時的作用域鏈,而不是函數(shù)執(zhí)行時 ,以上這個例子就是閉包。閉包主要有 兩個 應(yīng)用場景:
1.函數(shù) 作為 返回值 ,上面的例子就是
2.函數(shù) 作為 參數(shù) 傳遞,看以下例子
通過文你將學(xué)到:
經(jīng)常聽到閉包這個詞兒,或者匿名函數(shù)自執(zhí)行,之類的。到底他們是一個東西嗎?
我不想扣定義,直接上例子。
看圖:
函數(shù)執(zhí)行完以后會銷毀(這里我就不談堆棧操作了理解圖就行了),然后各種變量會垃圾回收,而這里parent函數(shù)確實銷毀了,但是firstName這個參數(shù)并沒有垃圾回收,釋放內(nèi)存,依然在內(nèi)存中能夠被return里面的函數(shù)使用,好像return里面的函數(shù)把 父函數(shù)的那個資源給關(guān)閉在了自己的函數(shù)里面一樣,這個函數(shù)銷毀資源被關(guān)閉到子函數(shù)中依然能夠使用的現(xiàn)象叫做閉包。
注意匿名函數(shù)自執(zhí)行只是產(chǎn)生閉包的一種情況,閉包是現(xiàn)象或者情形,不實用匿名函數(shù)自執(zhí)行也有很多情況產(chǎn)生閉包,所以而且根本就是兩回事兒,不能混淆。
類比,在window系統(tǒng)中,你子文件夾中有使用的文件父文件夾是沒法刪除的。
與這個類似的一個題是循環(huán)里面用事件,事件里面的i有問題 ,如下。
還有一到非常愛考的面試題,
我只分析一個,其它的大家就會分析了。注意表象上粗略的理解就是 函數(shù)執(zhí)行一瞬間,并不會等定時器,但是這個說法并不對,因為第一個就說不通。好我給記大家進入內(nèi)部深入分析下過程。
小測驗,你能看出下面的程序用了閉包嗎?
3.閉包在jquery中使用。
閉包無處不在,直接看jquery的例子。
寫一個回調(diào)函數(shù),通過回調(diào)方法返回值,function ajaxUser(function(val){ console.log(val);}){ ... }
jquery是一個輕量級的JS框架,這點相信大部分人都聽過,而jquery之所以有這樣一個稱呼,就是因為它悄悄披了一件外衣,將自己給隱藏了起來。
//以下截取自jquery源碼片段(function( window, undefined ) { ? /* ? ?源碼內(nèi)容 ? ?*/})( window );
上面這一小段代碼來自于1.9.0當(dāng)中jquery的源碼,它是一個無污染的JS插件的標(biāo)準(zhǔn)寫法,專業(yè)名詞叫閉包??梢园阉唵蔚目醋鍪且粋€函數(shù),與普通函數(shù)不同的是,這個函數(shù)沒有名字,而且會立即執(zhí)行,就像下面這樣,會直接彈出字符串。
(function( window, undefined ) {
alert("Hello World!");
})( window );
可以看出來這樣寫的直接效果,就相當(dāng)于我們直接彈出一個字符串。但是不同的是,我們將里面的變量變成了局域變量,這不僅可以提高運行速度,更重要的是我們在引用jquery的JS文件時,不會因為jquery當(dāng)中的變量太多,而與其它的JS框架的變量命名產(chǎn)生沖突。對于這一點,我們拿以下這一小段代碼來說明。
var temp = "Hello World!";
(function( window, undefined ) { ? ? ? var temp = "ByeBye World!";
})( window );
alert(temp);
這段代碼的運行結(jié)果是Hello而不是ByeBye,也就是說閉包中的變量聲明沒有污染到外面的全局變量,倘若我們?nèi)サ糸]包,則最終的結(jié)果會是ByeBye,就像下面這樣。
var temp = "Hello World!";// ? ?(function( window, undefined ) {
var temp = "ByeBye World!";// ? ?})( window );
alert(temp);
由此就可以看出來,jquery的外衣就是這一層閉包,它是很重要的一個內(nèi)容,是編寫JS框架必須知道的知識,它可以幫助我們隱藏我們的臨時變量,降低污染。
(function( window, undefined ) {
// Use the correct document accordingly with window argument (sandbox)
var document = window.document,
navigator = window.navigator,
location = window.location;
var jQuery = (function() { //第一個jQuery的作用是什么呢?是要給外面用的jQuery對象
// Define a local copy of jQuery
var jQuery = function( ) { //為什么又要一個jQuery?這是一個局部變量
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( ); //怎么才能保證程序執(zhí)行這個代碼呢?順序執(zhí)行下來就會執(zhí)行這個代碼
},
//...
;
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
//...
return jQuery.makeArray( selector, this );
},
//...
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn; //這段代碼的作用是什么呢?為以后的實例準(zhǔn)備jQuery原型構(gòu)造函數(shù)
//jQuery.fn.init 是構(gòu)造函數(shù), 它的實例方法由 jQuery.fn.init.prototype 指定
//...很多函數(shù)
return jQuery; //為什么要增加一個返回值呢?為了把局部變量復(fù)制給外面的全局變量
})();
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
})(window);
閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。由于在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數(shù)內(nèi)部的函數(shù)"。所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。
擴展資料:
閉包包含自由(未綁定到特定對象)變量;這些變量不是在這個代碼塊內(nèi)或者任何全局上下文中定義的,而是在定義代碼塊的環(huán)境中定義(局部變量)。
"閉包" 一詞來源于以下兩者的結(jié)合:要執(zhí)行的代碼塊(由于自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和為自由變量提供綁定的計算環(huán)境(作用域)。在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等語言中都能找到對閉包不同程度的支持。