摘要:說(shuō)了半天,究竟什么是閉包呢閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會(huì)繼續(xù)存在。彈出上面函數(shù)中的函數(shù)就是閉包,就是通過(guò)建立函數(shù)來(lái)訪(fǎng)問(wèn)函數(shù)內(nèi)部的局部變量。閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。
JavaScript的閉包
首先聲明,這是一篇面向小白的博客,不過(guò)也歡迎各位大牛批評(píng)指正,謝謝。
??其實(shí)關(guān)于閉包各個(gè)論壇社區(qū)里都有很多的文章來(lái)講它,畢竟閉包是JavaScript中一個(gè)特色,也正因?yàn)檫@個(gè)雨中不同的特色也讓閉包理解起來(lái)有一些吃力。筆者在這里不僅僅是想介紹閉包,也向列舉一些筆者所見(jiàn)過(guò)的一些閉包,如果有讀者還有一些比較經(jīng)典的閉包例子,希望可以在評(píng)論區(qū)里留一下,謝謝。
說(shuō)了半天,究竟什么是閉包呢?
閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會(huì)繼續(xù)存在。
閉包就是就是函數(shù)的“堆?!痹诤瘮?shù)返回后并不釋放,我們也可以理解為這些函數(shù)堆棧并不在棧上分配而是在堆上分配。
當(dāng)在一個(gè)函數(shù)內(nèi)定義另外一個(gè)函數(shù)就會(huì)產(chǎn)生閉包。
為了便于理解,我們可以簡(jiǎn)單的將閉包理解為:
閉包:是指有權(quán)訪(fǎng)問(wèn)另外一個(gè)函數(shù)作用域中的變量的函數(shù)。
JavaScript中的作用域JavaScript中是沒(méi)有塊級(jí)作用域的。不過(guò)關(guān)于塊級(jí)作用域我們?cè)谶@里不做深入探究,筆者在http://segmentfault.com/a/1190000004092842M中有對(duì)塊級(jí)作用域較為詳細(xì)的解釋?zhuān)欢淖x者可以去看看。
變量的作用域無(wú)非就是兩種:全局變量和局部變量。
Javascript語(yǔ)言的特殊之處,就在于函數(shù)內(nèi)部可以直接讀取全局變量。
var n=999; function f1(){ alert(n); } f1(); // 999
如上函數(shù),f1可調(diào)用全局變量n
另一方面,在函數(shù)外部自然無(wú)法讀取函數(shù)內(nèi)的局部變量。
function f1(){ var n=999; } alert(n); // error
這里有一個(gè)地方需要注意,函數(shù)內(nèi)部聲明變量的時(shí)候,一定要使用var命令。如果不用的話(huà),你實(shí)際上聲明了一個(gè)全局變量。
function f1(){ n=999; } f1(); alert(n); // 999閉包
1. 理解閉包
我們已經(jīng)理解了什么是作用域,什么是塊級(jí)作用域,那又該如何去訪(fǎng)問(wèn)函數(shù)內(nèi)部的變量呢?
出于種種原因,我們有時(shí)候需要得到函數(shù)內(nèi)的局部變量。但是,前面已經(jīng)說(shuō)過(guò)了,正常情況下,這是辦不到的,只有通過(guò)變通方法才能實(shí)現(xiàn)。
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result();// 彈出999
上面函數(shù)中的f2函數(shù)就是閉包,就是通過(guò)建立函數(shù)來(lái)訪(fǎng)問(wèn)函數(shù)內(nèi)部的局部變量。
2. 閉包的用途
閉包可以用在許多地方。它的最大用處有兩個(gè),一個(gè)是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
在這段代碼中,result實(shí)際上就是閉包f2函數(shù)。它一共運(yùn)行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒(méi)有在f1調(diào)用后被自動(dòng)清除。
為什么會(huì)這樣呢?原因就在于f1是f2的父函數(shù),而f2被賦給了一個(gè)全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴(lài)于f1,因此f1也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
這段代碼中另一個(gè)值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒(méi)有使用var關(guān)鍵字,因此nAdd是一個(gè)全局變量,而不是局部變量。其次,nAdd的值是一個(gè)匿名函數(shù)(anonymous function),而這個(gè)匿名函數(shù)本身也是一個(gè)閉包,所以nAdd相當(dāng)于是一個(gè)setter,可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的局部變量進(jìn)行操作。
3. 閉包的注意點(diǎn)
1)由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問(wèn)題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
2)閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。
4. 經(jīng)典閉包小案例
如果你能理解下面全部的案例,那你的閉包就算是真正掌握了。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());//The Window
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());//My Object
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
//問(wèn):三行a,b,c的輸出分別是什么?
這是一道非常典型的JS閉包問(wèn)題。其中嵌套了三層fun函數(shù),搞清楚每層fun的函數(shù)是那個(gè)fun函數(shù)尤為重要。
//答案:
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
都答對(duì)了么?如果都答對(duì)了恭喜你在js閉包問(wèn)題當(dāng)中幾乎沒(méi)什么可以難住你了。
Happy hacking!文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/49689.html
摘要:說(shuō)了半天,究竟什么是閉包呢閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會(huì)繼續(xù)存在。彈出上面函數(shù)中的函數(shù)就是閉包,就是通過(guò)建立函數(shù)來(lái)訪(fǎng)問(wèn)函數(shù)內(nèi)部的局部變量。閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。 JavaScript的閉包 首先聲明,這是一篇面向小白的博客,不過(guò)也歡迎各位大牛批評(píng)指正,謝謝。 ??其實(shí)關(guān)于閉包各個(gè)論壇社區(qū)里都有很多的文章來(lái)講它,畢竟閉包是JavaScri...
摘要:圖片中的作用域鏈,是全局執(zhí)行環(huán)境中的作用域鏈。然后此活動(dòng)對(duì)象被推入作用域鏈的最前端。在最后調(diào)用的時(shí)候,創(chuàng)建先構(gòu)建作用域鏈,再創(chuàng)建執(zhí)行環(huán)境,再創(chuàng)建執(zhí)行環(huán)境的時(shí)候發(fā)現(xiàn)了一個(gè)變量標(biāo)識(shí)符。 從圖書(shū)館翻過(guò)各種JS的書(shū)之后,對(duì)作用域/執(zhí)行環(huán)境/閉包這些概念有了一個(gè)比較清晰的認(rèn)識(shí)。 栗子說(shuō)明一切 第一個(gè)栗子 來(lái)看一個(gè)來(lái)自ECMA-262的栗子: var x = 10; (function foo(...
摘要:事件循環(huán)了解知識(shí)點(diǎn)線(xiàn)程執(zhí)行棧線(xiàn)程是單線(xiàn)程的語(yǔ)言可以單線(xiàn)程將理解為只有一條車(chē)道在車(chē)道里后面的車(chē)在等前面的車(chē)通過(guò)后才能通過(guò)即當(dāng)前面的程序沒(méi)有執(zhí)行后面的程序也不能執(zhí)行執(zhí)行棧執(zhí)行棧像車(chē)道被執(zhí)行的程序會(huì)放入執(zhí)行棧里但它的執(zhí)行的順序是后面進(jìn)來(lái)的程序先執(zhí) 事件循環(huán) 了解知識(shí)點(diǎn) 線(xiàn)程 執(zhí)行棧 task queue web api macro task micro task 線(xiàn)程 javascrip...
摘要:實(shí)現(xiàn)閉包將匿名函數(shù)在普通函數(shù)中當(dāng)做參數(shù)傳入,也可以被返回。如果將匿名函數(shù)返回給外界,匿名函數(shù)會(huì)保存所引用的變量,而外界則不能得到這些變量,這樣形成閉包這個(gè)概念可能會(huì)更清晰一些。 原文:http://my.oschina.net/melonol/blog/126694 匿名函數(shù)提到閉包就不得不想起匿名函數(shù),也叫閉包函數(shù)(closures),貌似PHP閉包實(shí)現(xiàn)主要就是靠它。聲明一個(gè)匿名函數(shù)...
摘要:和深入理解在和深入理解這篇博客里筆者曾做過(guò)總結(jié),我們知道試單線(xiàn)程的產(chǎn)物,兩個(gè)函數(shù)就是利用了插入代碼的方式實(shí)現(xiàn)了偽異步,和的原理實(shí)際上是一樣的。綜上所述,其實(shí)終歸是單線(xiàn)程產(chǎn)物。無(wú)論如何異步都不可能突破單線(xiàn)程這個(gè)障礙。 說(shuō)明:??這是筆者平時(shí)積累的一些覺(jué)得比較有意思或是比較有難度的JavaScript題目理解和心得,會(huì)保持長(zhǎng)期更新。 1.setTimeout和setInterval深入理解...
閱讀 2879·2021-10-14 09:43
閱讀 1673·2021-09-29 09:34
閱讀 1757·2021-07-28 00:16
閱讀 2972·2019-08-30 15:53
閱讀 2917·2019-08-30 13:59
閱讀 2972·2019-08-30 13:57
閱讀 1102·2019-08-26 13:38
閱讀 1906·2019-08-26 13:25