JavaScript進(jìn)階(三)閉包原理與用法詳解
本文實(shí)例講述了JavaScript閉包原理與用法。分享給大家供大家參考,具體如下:
為了更好的理解,在閱讀此文之前建議先閱讀上一篇《JavaScript詞法作用域與作用域鏈》 1.什么是閉包閉包的含義就是閉合,包起來(lái),簡(jiǎn)單的來(lái)說(shuō),就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是一個(gè)具有封閉的對(duì)外不公開(kāi)的,包裹結(jié)構(gòu),或空間。
在JS中函數(shù)構(gòu)成閉包。一般函數(shù)是一個(gè)代碼結(jié)構(gòu)的封閉結(jié)構(gòu),即包裹的特性,同時(shí)根據(jù)作用域規(guī)則只允許函數(shù)訪問(wèn)外部的數(shù)據(jù),外部無(wú)法訪問(wèn)函數(shù)內(nèi)部的數(shù)據(jù),即封閉的對(duì)外不公開(kāi)的特性,因此說(shuō)函數(shù)可以構(gòu)成閉包。
概括:閉包就是一個(gè)具有封閉與包裹功能的結(jié)構(gòu)。函數(shù)可以構(gòu)成閉包。函數(shù)內(nèi)部定義的數(shù)據(jù)函數(shù)外部無(wú)法訪問(wèn),即函數(shù)具有封閉性;函數(shù)可以封裝代碼即具有包裹性,所以函數(shù)可以構(gòu)成閉包。2.閉包有什么用(解決什么問(wèn)題)? 閉包不允許外部訪問(wèn) 要解決的問(wèn)題就是間接訪問(wèn)該數(shù)據(jù)函數(shù)就可以構(gòu)成閉包,要解決的問(wèn)題就是如何訪問(wèn)到函數(shù)內(nèi)部的數(shù)據(jù)
function foo () { var num = 123; return num;}var res = foo();console.log( res ); // =>123
這里的確是訪問(wèn)到函數(shù)中的數(shù)據(jù)了。但是該數(shù)據(jù)不能第二次訪問(wèn),因此第二次訪問(wèn)的時(shí)候又要調(diào)用一次foo,表示又有一個(gè)新的num = 123出來(lái)了。
在函數(shù)內(nèi)的數(shù)據(jù),不能直接在函數(shù)外部訪問(wèn),那么在函數(shù)內(nèi)如果定義一個(gè)函數(shù),那么在這個(gè)函數(shù)內(nèi)部中是可以直接訪問(wèn)的
function foo() { var num = Math.random(); function func() { return mun; } return func;}var f = foo();// f 可以直接訪問(wèn)這個(gè) numvar res1 = f();var res2 = f();
我們使用前面學(xué)習(xí)的繪制作用域鏈結(jié)構(gòu)圖的方法來(lái)繪制閉包的作用域鏈結(jié)構(gòu)圖,如下:
如何獲得超過(guò)一個(gè)數(shù)據(jù)
function foo () { var num1 = Math.random(); var num2 = Math.random(); return { num1: function () { return num1; }, num2: function () { return num2; } }}
如何完成讀取一個(gè)數(shù)據(jù)和修改這個(gè)數(shù)據(jù)
function foo () { var num = Math.random(); return { get_num : function () { return num; }, set_num: function( value ) { return num = value; } }}4.基本的閉包結(jié)構(gòu)
一般閉包的問(wèn)題就是要想辦法簡(jiǎn)潔的獲取函數(shù)內(nèi)的數(shù)據(jù)使用權(quán),那么我們就可以總結(jié)出一個(gè)基本的使用模型。
寫(xiě)一個(gè)函數(shù),函數(shù)內(nèi)部定義一個(gè)新函數(shù),返回新函數(shù),用新函數(shù)獲得函數(shù)內(nèi)的數(shù)據(jù) 寫(xiě)一個(gè)函數(shù),函數(shù)內(nèi)部定義個(gè)一個(gè)對(duì)象,對(duì)象中綁定多個(gè)函數(shù)(方法),返回對(duì)象,利用對(duì)象的方法訪問(wèn)函數(shù)內(nèi)的數(shù)據(jù)5.閉包的基本用法閉包是為了實(shí)現(xiàn)具有私有訪問(wèn)空間的函數(shù)的
帶有私有訪問(wèn)數(shù)據(jù)的對(duì)象
function Person() { this.name = '張三'; // setName( ’’ )}
所有的私有數(shù)據(jù),就是說(shuō)只有函數(shù)內(nèi)部可以訪問(wèn)的數(shù)據(jù),或?qū)ο髢?nèi)部的方法可以訪問(wèn)的數(shù)據(jù)
最簡(jiǎn)單的實(shí)現(xiàn):
function createPerson() { var __name__ = ''; return { getName: function () { return __name__; }, setName: function( value ) { // 如果不姓張就報(bào)錯(cuò) if ( value.charAt(0) === ’張’ ) { __name__ = value; } else { throw new Error( ’姓氏不對(duì),不能取名’ ); } } }}var p = createPerson();p.set_Name( ’張三豐’ );console.log( p.get_Name() );p.set_Name( ’張王富貴’ );console.log( p.get_Name() );
帶有私有數(shù)據(jù)的函數(shù)
var func = function () {}function func () {}var foo = (function () { // 私有數(shù)據(jù) return function () { // 可以使用私有的數(shù)據(jù) ... };});6.閉包基本模型
對(duì)象模型
function foo () { // 私有數(shù)據(jù) return { method : function(){ // 操作私有數(shù)據(jù) } }}
函數(shù)模型
function foo(){ // 私有數(shù)據(jù) return function(){ // 可以操作私有數(shù)據(jù) }}7.沙箱模式(閉包應(yīng)用的一個(gè)典范)7.1 沙箱的概念
沙盤(pán)與盒子,就可以在一個(gè)笑笑的空間內(nèi)模擬顯示世界,特點(diǎn)是執(zhí)行效果與現(xiàn)實(shí)世界一模一樣,但是在沙箱中模擬與現(xiàn)實(shí)無(wú)關(guān).
7.2 沙箱模式沙箱模式就是一個(gè)自調(diào)用函數(shù),代碼寫(xiě)到函數(shù)中一樣會(huì)執(zhí)行,但是不會(huì)與外界有任何的影響
例如,在jQuery中
(function () { var jQuery = function () { // 所有的算法 } // .... // .... jQuery.each = function () {} window.jQuery = window.$ = jQuery;})();$.each( ... )8.帶有緩存功能的函數(shù)
以 Fibonacci 數(shù)列為例,改進(jìn)傳統(tǒng)計(jì)算斐波那契數(shù)列方法 我們來(lái)回顧一下傳統(tǒng)遞歸方式求斐波那契數(shù)列方法,我們定義一個(gè)count變量來(lái)查看遞歸了多少次:
var count = 0;function fibo( n ){ count++; if( n ==0 || n == 1 ) return 1; return fibo( n - 1 ) + fibo( n - 2 );}fib1( 20 );console.log( count1 );// 5: 15// 6: 25// ...// 20: 21891
當(dāng) n = 5 式,count = 15,當(dāng)時(shí)當(dāng) n = 20 的時(shí)候,count就達(dá)到驚人的21891次,性能太低了
性能低的原因是 重復(fù)計(jì)算。如果每次將計(jì)算的結(jié)果存起來(lái)
那么每次需要的時(shí)候先看看有沒(méi)有存儲(chǔ)過(guò)該數(shù)據(jù),如果有,直接拿來(lái)用。 如果沒(méi)有再遞歸,但是計(jì)算的結(jié)果需要再次存儲(chǔ)起來(lái),以便下次使用改進(jìn)版:
var data = [ 1, 1 ];var count = 0;function fibo( n ) { count++; var v = data[ n ]; if( v === undefined ){ v = fibo( n - 1 ) + fibo( n - 2 ); data[ n ] = v; } return v;}fibo( 100 );console.log( count ); // 199
改進(jìn)之后, n = 100的時(shí)候也才199次,大大提高了性能。
9.閉包的性能問(wèn)題函數(shù)執(zhí)行需要內(nèi)存,那么函數(shù)中定義的變量,會(huì)在函數(shù)執(zhí)行結(jié)束后自動(dòng)回收,凡是因?yàn)殚]包結(jié)構(gòu)的,被引出的數(shù)據(jù),如果還有變量引用這些數(shù)據(jù)的話,那么這些數(shù)據(jù)就不會(huì)被回收。
因此在使用閉包的時(shí)候如果不適用某學(xué)數(shù)據(jù)了,一定要賦值一個(gè)null
var f = (function () { var num = 123; return function () { return num; };})();// f 引用著函數(shù),函數(shù)引用著變量num// 因此在不適用該數(shù)據(jù)的時(shí)候,最好寫(xiě)上f = null;
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼運(yùn)行效果。
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《JavaScript常用函數(shù)技巧匯總》、《javascript面向?qū)ο笕腴T(mén)教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章:
1. 利用CSS制作3D動(dòng)畫(huà)2. 讀大數(shù)據(jù)量的XML文件的讀取問(wèn)題3. 存儲(chǔ)于xml中需要的HTML轉(zhuǎn)義代碼4. 使用Spry輕松將XML數(shù)據(jù)顯示到HTML頁(yè)的方法5. html5手機(jī)觸屏touch事件介紹6. 用xslt+css讓RSS顯示的跟網(wǎng)頁(yè)一樣漂亮7. 《CSS3實(shí)戰(zhàn)》筆記--漸變?cè)O(shè)計(jì)(一)8. 測(cè)試模式 - XSL教程 - 59. CSS3實(shí)現(xiàn)動(dòng)態(tài)翻牌效果 仿百度貼吧3D翻牌一次動(dòng)畫(huà)特效10. HTML5 Canvas繪制圖形從入門(mén)到精通
