什么是閉包铆农,閉包的優(yōu)缺點(diǎn)牺氨,閉包的使用場景

概念

閉包是指有權(quán)訪問另外一個函數(shù)作用域中的變量的函數(shù)

// 1、箭頭函數(shù)體的閉包( i=0 是默認(rèn)參數(shù))
var Add = (i = 0) => {
    return (() => (++i))
};
var v = Add();
v();  //1
v();  //2

閉包的優(yōu)點(diǎn)

可以重復(fù)使用變量墩剖,并且不會造成變量污染

  • 全局變量可以重復(fù)使用猴凹,但是容易造成變量污染。局部變量僅在局部作用域內(nèi)有效岭皂,不可以重復(fù)使用郊霎,不會造成變量污染。閉包結(jié)合了全局變量和局部變量的優(yōu)點(diǎn)爷绘。

可以用來定義私有屬性和私有方法书劝。

閉包的缺點(diǎn)

比普通函數(shù)更占用內(nèi)存,會導(dǎo)致網(wǎng)頁性能變差土至,在IE下容易造成內(nèi)存泄露购对。

什么是內(nèi)存泄漏

首先,需要了解瀏覽器自身的內(nèi)存回收機(jī)制陶因。
每個瀏覽器會有自己的一套回收機(jī)制骡苞,當(dāng)分配出去的內(nèi)存不使用的時候便會回收;內(nèi)存泄露的根本原因就是你的代碼中分配了一些‘頑固的’內(nèi)存楷扬,瀏覽器無法進(jìn)行回收解幽,如果這些’頑固的’內(nèi)存還在一直不停地分配就會導(dǎo)致后面所用內(nèi)存不足,造成泄露烘苹。

閉包造成內(nèi)存泄漏

因為閉包就是能夠訪問外部函數(shù)變量的一個函數(shù)躲株,而函數(shù)是必須保存在內(nèi)存中的對象,所以位于函數(shù)執(zhí)行上下文中的所有變量也需要保存在內(nèi)存中镣衡,這樣就不會被回收徘溢,如果一旦循環(huán)引用或創(chuàng)建閉包吞琐,就會占據(jù)大量內(nèi)存,可能會引起內(nèi)存泄漏

內(nèi)存泄漏的解決方案

造成內(nèi)存泄露的原因:
  • 意外的全局變量(在函數(shù)內(nèi)部沒有使用var進(jìn)行聲明的變量)
  • console.log
  • 閉包
  • 對象的循環(huán)引用
  • 未清除的計時器
  • DOM泄露(獲取到DOM節(jié)點(diǎn)之后然爆,將DOM節(jié)點(diǎn)刪除,但是沒有手動釋放變量黍图,拿對應(yīng)的DOM節(jié)點(diǎn)在變量中還可以訪問到曾雕,就會造成泄露)
如何避免閉包引起的內(nèi)存泄漏:
  1. 在退出函數(shù)之前,將不使用的局部變量全部刪除助被,可以使變量賦值為null
//這段代碼會導(dǎo)致內(nèi)存泄露
    window.onload = function(){
        var el = document.getElementById("id");
        el.onclick = function(){
            alert(el.id);
        }
    }

//解決方法為
    window.onload = function(){
        var el = document.getElementById("id");
        var id = el.id; //解除循環(huán)引用
        el.onclick = function(){
            alert(id); 
        }
        el = null; // 將閉包引用的外部函數(shù)中活動對象清除
    }
  1. 避免變量的循環(huán)賦值和引用(代碼如上)
  2. 由于jQuery考慮到了內(nèi)存泄漏的潛在危害剖张,所以它會手動釋放自己指定的所有事件處理程序。只要堅持使用jQuery的事件綁定方法揩环,就可以一定程度上避免這種特定的常見原因?qū)е碌膬?nèi)存泄漏搔弄。
//這段代碼會導(dǎo)致內(nèi)存泄露
$(document).ready(function() {
    var button = document.getElementById('button-1');
    button.onclick = function() {
         console.log('hello');
         return false;
    };
});

//當(dāng)指定單擊事件處理程序時,就創(chuàng)建了一個在其封閉的環(huán)境中包含button變量的閉包丰滑。而且顾犹,現(xiàn)在的button也包含一個指向閉包(onclick屬性自身)的引用。這樣褒墨,就導(dǎo)致了在IE中即使離開當(dāng)前頁面也不會釋放這個循環(huán)炫刷。

//用jQuery化解引用循環(huán)
$(document).ready(function() {
    var $button = $('#button-1');
    $button.click(function(event) {
        event.preventDefault();
        console.log('hello');
    });
});

閉包的使用場景

封裝功能時(需要使用私有的屬性和方法),函數(shù)防抖郁妈、函數(shù)節(jié)流浑玛、函數(shù)柯里化、給元素偽數(shù)組添加事件需要使用元素的索引值噩咪。

閉包實(shí)例--函數(shù)防抖

控制函數(shù)執(zhí)行時機(jī)

/**
 * @function debounce 函數(shù)防抖
 * @param {Function} fn 需要防抖的函數(shù)
 * @param {Number} interval 間隔時間
 * @return {Function} 經(jīng)過防抖處理的函數(shù)
 * */
function debounce(fn, interval) {
    let timer = null; // 定時器
    return function() {
        // 清除上一次的定時器
        clearTimeout(timer);
        // 拿到當(dāng)前的函數(shù)作用域
        let _this = this;
        // 拿到當(dāng)前函數(shù)的參數(shù)數(shù)組
        let args = Array.prototype.slice.call(arguments, 0);
        // 開啟倒計時定時器
        timer = setTimeout(function() {
            // 通過apply傳遞當(dāng)前函數(shù)this顾彰,以及參數(shù)
            fn.apply(_this, args);
            // 默認(rèn)300ms執(zhí)行
        }, interval || 300)
    }
}
概念:

就是指觸發(fā)事件后在 n 秒內(nèi)函數(shù)只能執(zhí)行一次,如果在 n 秒內(nèi)又觸發(fā)了事件胃碾,則會重新計算函數(shù)執(zhí)行時間涨享。
通俗一點(diǎn):在一段固定的時間內(nèi),只能觸發(fā)一次函數(shù)书在,在多次觸發(fā)事件時灰伟,只執(zhí)行最后一次。

使用時機(jī):
  • 搜索功能儒旬,在用戶輸入結(jié)束以后才開始發(fā)送搜索請求栏账,可以使用函數(shù)防抖來實(shí)現(xiàn);

閉包實(shí)例--函數(shù)節(jié)流

控制函數(shù)執(zhí)行頻率

/**
 * @function throttle 函數(shù)節(jié)流
 * @param {Function} fn 需要節(jié)流的函數(shù)
 * @param {Number} interval 間隔時間
 * @return {Function} 經(jīng)過節(jié)流處理的函數(shù)
 * */
function throttle(fn, interval) {
    let timer = null; // 定時器
    let firstTime = true; // 判斷是否是第一次執(zhí)行
    // 利用閉包
    return function() {
        // 拿到函數(shù)的參數(shù)數(shù)組
        let args = Array.prototype.slice.call(arguments, 0);
        // 拿到當(dāng)前的函數(shù)作用域
        let _this = this;
        // 如果是第一次執(zhí)行的話栈源,需要立即執(zhí)行該函數(shù)
        if(firstTime) {
            // 通過apply挡爵,綁定當(dāng)前函數(shù)的作用域以及傳遞參數(shù)
            fn.apply(_this, args);
            // 修改標(biāo)識為null,釋放內(nèi)存
            firstTime = null;
        }
        // 如果當(dāng)前有正在等待執(zhí)行的函數(shù)則直接返回
        if(timer) return;
        // 開啟一個倒計時定時器
        timer = setTimeout(function() {
            // 通過apply甚垦,綁定當(dāng)前函數(shù)的作用域以及傳遞參數(shù)
            fn.apply(_this, args);
            // 清除之前的定時器
            timer = null;
            // 默認(rèn)300ms執(zhí)行一次
        }, interval || 300)
    }
}
概念

就是限制一個函數(shù)在一定時間內(nèi)只能執(zhí)行一次茶鹃。

使用時機(jī)
  • 改變?yōu)g覽器窗口尺寸涣雕,可以使用函數(shù)節(jié)流,避免函數(shù)不斷執(zhí)行闭翩;
  • 滾動條scroll事件挣郭,通過函數(shù)節(jié)流,避免函數(shù)不斷執(zhí)行疗韵。
函數(shù)節(jié)流與函數(shù)防抖的區(qū)別:

我們以一個案例來講一下它們之間的區(qū)別:
設(shè)定一個間隔時間為一秒兑障,在一分鐘內(nèi),不斷的移動鼠標(biāo)蕉汪,讓它觸發(fā)一個函數(shù)流译,打印一些內(nèi)容。

  • 函數(shù)防抖:會打印1次者疤,在鼠標(biāo)停止移動的一秒后打印福澡。
  • 函數(shù)節(jié)流:會打印60次,因為在一分鐘內(nèi)有60秒驹马,每秒會觸發(fā)一次革砸。
  • 總結(jié):節(jié)流是為了限制函數(shù)的執(zhí)行次數(shù),而防抖是為了限制函數(shù)的執(zhí)行時機(jī)窥翩。
函數(shù)節(jié)流與函數(shù)防抖的使用

此處使用一個對象的方法业岁,主要為了測試this指向綁定的問題,調(diào)用的時候傳遞參數(shù)問題等寇蚊。

function log(a,b) {
    console.log(a,b);
    console.log(this);
}
const throttleLog = throttle(log, 1000);
const debounceLog = debounce(log, 1000);
let a  = {
    b: throttleLog,
    c: debounceLog
};
document.body.onmousemove = function() {
    a.b('throttle', 'log');
    a.c('debounce', 'log');
};

閉包實(shí)例--函數(shù)柯里化

閉包實(shí)例--給元素偽數(shù)組添加事件

// DOM操作
let li = document.querySelectorAll('li');
for(var i = 0; i < li.length; i++) {
    (function(i){
        li[i].onclick = function() {
            alert(i);
        }
    })(i)
}

閉包實(shí)例--不使用循環(huán)返回數(shù)組

function getArr() {
    let num = 10;
    let arr = [];
    return (function(){
        arr.unshift(num);
        num--;
        if(num > 0) {
            arguments.callee();
        }
        return arr;
    })()
}
console.log(getArr());  //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笔时,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子仗岸,更是在濱河造成了極大的恐慌允耿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扒怖,死亡現(xiàn)場離奇詭異较锡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)盗痒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門蚂蕴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俯邓,你說我怎么就攤上這事骡楼。” “怎么了稽鞭?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵鸟整,是天一觀的道長。 經(jīng)常有香客問我朦蕴,道長篮条,這世上最難降的妖魔是什么弟头? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮涉茧,結(jié)果婚禮上赴恨,老公的妹妹穿的比我還像新娘。我一直安慰自己降瞳,他們只是感情好嘱支,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挣饥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沛膳。 梳的紋絲不亂的頭發(fā)上扔枫,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音锹安,去河邊找鬼短荐。 笑死,一個胖子當(dāng)著我的面吹牛叹哭,可吹牛的內(nèi)容都是我干的忍宋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼风罩,長吁一口氣:“原來是場噩夢啊……” “哼糠排!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起超升,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤入宦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后室琢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乾闰,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年盈滴,在試婚紗的時候發(fā)現(xiàn)自己被綠了涯肩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡巢钓,死狀恐怖病苗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竿报,我是刑警寧澤铅乡,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站烈菌,受9級特大地震影響阵幸,放射性物質(zhì)發(fā)生泄漏花履。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一挚赊、第九天 我趴在偏房一處隱蔽的房頂上張望诡壁。 院中可真熱鬧,春花似錦荠割、人聲如沸妹卿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夺克。三九已至,卻和暖如春嚎朽,著一層夾襖步出監(jiān)牢的瞬間铺纽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工哟忍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狡门,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓锅很,卻偏偏與公主長得像其馏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子爆安,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355