理解js中的閉包以及應(yīng)用場景

在js中的學習中,總會遇到一個陌生又晦澀椿访,然后還是陌生的詞匯乌企,那就是閉包。

首先成玫,什么是閉包加酵?
其次,閉包的作用是什么呢哭当?
最后猪腕,什么時候用得到閉包呢?

接下來钦勘,我們就來回答這三個問題陋葡。

1.什么是閉包?

首先个盆,閉包的概念:

閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)脖岛《淦埽可以理解成“定義在一個函數(shù)內(nèi)部的函數(shù)“颊亮。在本質(zhì)上,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁陨溅。

看完這段話终惑,是不是覺得?门扇?雹有?
那么偿渡,來看一個例子:
想要實現(xiàn)一個函數(shù),每次調(diào)用的時候霸奕,函數(shù)內(nèi)的變量實現(xiàn)加1溜宽。

function fn(){
    var a = 10; 
    a++;
    console.log(a);

}
var f = fn();  
f();   
f(); 
f(); 
f();

這樣,每次調(diào)用fn质帅,得到的a都是11 适揉。這是因為每次調(diào)用的時候,都會重新聲明a煤惩。若是不想每次獲取到都是同一個值嫉嘀,每次就不能重新聲明變量,這樣魄揉,我們就可以將聲明放在函數(shù)的外面誓沸,即:

var a = 10; 
function fn(){
    a++;
    console.log(a);

}
var f = fn();  
f();   
f(); 
f(); 
f();

但是峰鄙,這樣的話,a就變成了全局變量,會造成全局變量命名空間的污染痊焊。所以,這個時候欣除,我們需要閉包兔朦。

function addOne(){
    var a = 10; 
// a是局部變量,使用閉包后摇零,a進化成addOne或者fn的私有(自由)變量推掸,在函數(shù)外面可以操作a的值,每次進行加1
    function fn(){
        a++;
        console.log(a);
    }
    return fn; 
}
var f = addOne();  // addOne只執(zhí)行一次驻仅,var a = 10只執(zhí)行一次谅畅,只定義了一次
f(); // 11  每次執(zhí)行fn,a都會加1
f(); // 12
f(); // 13
f(); // 14

現(xiàn)在是不是覺得對閉包有點感覺了呢噪服?
在闡述閉包作用之前毡泻,值得一提的是閉包的原理。那么粘优,我們來說一下閉包的原理:

1.計算機原理:垃圾回收機制

計算機的垃圾回收機制:將要刪除的信息暫存仇味,下次可以再次使用,而不是立即刪除
在閉包中的體現(xiàn):利用作用域的嵌套,臨時保存即將刪除的變量雹顺,觸發(fā)計算機的垃圾回收機制

2.函數(shù)的執(zhí)行原理:
兩個概念:定義作用域丹墨,執(zhí)行作用域

顧名思義,定義作用域就是函數(shù)定義的作用域嬉愧,執(zhí)行作用域就是函數(shù)在執(zhí)行時所在的作用域贩挣。
在js中規(guī)定:函數(shù)在執(zhí)行時,可以讀取自身定義作用域中的變量

2.閉包的作用是?

(1)局部變量可以在全局空間內(nèi)操作

(2)將可能被刪除或覆蓋的局部變量,臨時保存王财,不被刪除或覆蓋

解釋完閉包的概念卵迂、原理、作用绒净,接下來就是我們最關(guān)心的见咒,什么時候使用閉包呢?

3.閉包的應(yīng)用場景挂疆?

場景1:for循環(huán)內(nèi)部函數(shù)需要獲取計數(shù)器

問題闡述:for循環(huán)之中的i變量會因為for的循環(huán)次數(shù)被覆蓋论颅,所以在for循環(huán)內(nèi)部存在函數(shù)時,而且這個函數(shù)內(nèi)會調(diào)用變量i囱嫩,這種情況下就需要用到閉包恃疯。
<body>
    <ul>
        <li>link1</li>
        <li>link2</li>
        <li>link3</li>
        <li>link4</li>
        <li>link5</li>
    </ul>
</body>
<script type="text/javascript">
var ali = document.querySelectorAll("li");
for(var i=0;i<ali.length;i++){
    (function(i){ 
        ali[i].onclick = function(){ 
            console.log(i); 
        }
    })(i);
}
</script>
解釋:for循環(huán)執(zhí)行很快,將每次的i的保存到匿名函數(shù)的實參中墨闲,通過匿名函數(shù)自動執(zhí)行傳給形參今妄,在匿名函數(shù)中可以獲取到i,點擊事件函數(shù)的定義作用域也是執(zhí)行作用域鸳碧,所以在事件執(zhí)行函數(shù)中可以獲取外層匿名函數(shù)的形參i,這樣每次就可以在事件函數(shù)中獲取到for循環(huán)的計數(shù)器i盾鳞。
友情提示:這里let也可以實現(xiàn),利用了let的塊級作用域可以形成閉包的原理瞻离,且let方式更簡單腾仅。
<body>
    <ul>
        <li>link1</li>
        <li>link2</li>
        <li>link3</li>
        <li>link4</li>
        <li>link5</li>
    </ul>
</body>
<script type="text/javascript">
var ali = document.querySelectorAll("li");
for(let i=0;i<ali.length;i++){
    ali[i].onclick = function(){ 
        console.log(i); 
    }
}
</script>

場景2:異步的回調(diào)函數(shù)中要讀取外部實時被修改的變量

smallFire(){
        for(var i=0;i<num;i++){
            let div = document.createElement("div");
            move(div,{
                left:t.x,
                top:t.y
            },function(){
                // 因為聲明小煙花時使用的let,會觸發(fā)塊級作用域,每次循環(huán)執(zhí)行時,move及自身的回調(diào)函數(shù)會隨之開啟,都處在對應(yīng)的塊級作用域內(nèi),所以能夠拿到每個塊級作用域的div,可以刪除
                div.remove();
            })  
        }
    }

解釋:因為循環(huán)會立即執(zhí)行,每次循環(huán)創(chuàng)建的小煙花,需要單獨保存(或單獨使用(運動和刪除)),所以為了防止循環(huán)創(chuàng)建的元素被覆蓋,使用let聲明變量,觸發(fā)塊級作用域,保存變量,方便將來操作套利。
下面是一個封裝好的緩沖運動的函數(shù):

function move(ele,data,cb){
    clearInterval(ele.t);
    ele.t = setInterval(function(){
        var onoff = true;
        for(var i in data){
            var iNow = parseInt(getStyle(ele,i));
            var speed = (data[i] - iNow)/10;
            speed = speed<0 ? Math.floor(speed) : Math.ceil(speed);
            if(iNow != data[i]){
                onoff = false;
            }
            ele.style[i] = iNow + speed + "px";
        }
        if(onoff){
            clearInterval(ele.t);
            cb && cb();
        }
    },30)
}

場景3:可以給無法處理函數(shù)參數(shù)的函數(shù),處理參數(shù)

需求:每隔3秒鐘推励,打印一個hello
問題1:這里say函數(shù)加了括號,就立即執(zhí)行了肉迫,不再等3秒了验辞。執(zhí)行之后,函數(shù)的返回值是undefined喊衫,結(jié)果setTimeout的第一個參數(shù)就是undefined跌造。
setTimeout(say("hello"),3000);  
function say(a){
    console.log(a);
}
問題2:所以say不能加括號,那么say就沒法傳參了
setTimeout(say,3000);  
function say(a){
    console.log(a);
}
解決:所以族购,say必須得加括號且傳參壳贪。這樣,延遲器的第一個必須是一個函數(shù)寝杖,所以在say函數(shù)中违施,返回一個函數(shù)即可,在函數(shù)中執(zhí)行想要的操作朝墩。
setTimeout(say("你好"),3000);
function say(a){
    return function(){
        console.log(a);
    }
}

這里醉拓,就利用到了函數(shù)的執(zhí)行作用域可以使用函數(shù)的定義作用域里面的變量這個原理伟姐。因為內(nèi)層函數(shù)的定義作用域是say(a){這里即是內(nèi)層函數(shù)的定義作用域收苏,可以拿到這里定義的所有的變量亿卤,包括say函數(shù)的形參a}。
看完應(yīng)用場景之后鹿霸,是不是覺得閉包還真有點意思排吴。然而,世上沒有十全十美的事物懦鼠,閉包也存在其缺點:
內(nèi)存消耗很大钻哩,不能濫用;閉包會在父函數(shù)外部肛冶,改變父函數(shù)內(nèi)部變量的值街氢。
是不是覺得意猶未盡?想挖掘更多關(guān)于閉包的特點呢睦袖?期待大家的分享
今天的分享到此結(jié)束珊肃,如果文中有什么錯誤或者不足之處,歡迎大家指正...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末馅笙,一起剝皮案震驚了整個濱河市伦乔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌董习,老刑警劉巖烈和,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異皿淋,居然都是意外死亡招刹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門窝趣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔗喂,“玉大人,你說我怎么就攤上這事高帖$侄” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵散址,是天一觀的道長乖阵。 經(jīng)常有香客問我,道長预麸,這世上最難降的妖魔是什么瞪浸? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮吏祸,結(jié)果婚禮上对蒲,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好蹈矮,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布砰逻。 她就那樣靜靜地躺著,像睡著了一般泛鸟。 火紅的嫁衣襯著肌膚如雪蝠咆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天北滥,我揣著相機與錄音刚操,去河邊找鬼。 笑死再芋,一個胖子當著我的面吹牛菊霜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播济赎,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼鉴逞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了联喘?” 一聲冷哼從身側(cè)響起华蜒,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎豁遭,沒想到半個月后叭喜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡蓖谢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年捂蕴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闪幽。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡啥辨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盯腌,到底是詐尸還是另有隱情溉知,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布腕够,位于F島的核電站级乍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏帚湘。R本人自食惡果不足惜玫荣,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望大诸。 院中可真熱鬧捅厂,春花似錦贯卦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盈厘,卻和暖如春睁枕,著一層夾襖步出監(jiān)牢的瞬間官边,已是汗流浹背沸手。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留注簿,地道東北人契吉。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像诡渴,于是被迫代替她去往敵國和親捐晶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容