[前端]什么是閉包,閉包的優缺點,閉包的應用場景


轉載:https://www.jianshu.com/p/d903be89f211

概念

閉包是指有權訪問另外一個函數作用域中的變量的函數

閉包的優點

可以重復使用變量,並且不會造成變量污染

  • 全局變量可以重復使用,但是容易造成變量污染。局部變量僅在局部作用域內有效,不可以重復使用,不會造成變量污染。閉包結合了全局變量和局部變量的優點。

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

閉包的缺點

比普通函數更占用內存,會導致網頁性能變差,在IE下容易造成內存泄露。

什么是內存泄漏

首先,需要了解瀏覽器自身的內存回收機制。
每個瀏覽器會有自己的一套回收機制,當分配出去的內存不使用的時候便會回收;內存泄露的根本原因就是你的代碼中分配了一些‘頑固的’內存,瀏覽器無法進行回收,如果這些’頑固的’內存還在一直不停地分配就會導致后面所用內存不足,造成泄露。

閉包造成內存泄漏

因為閉包就是能夠訪問外部函數變量的一個函數,而函數是必須保存在內存中的對象,所以位於函數執行上下文中的所有變量也需要保存在內存中,這樣就不會被回收,如果一旦循環引用或創建閉包,就會占據大量內存,可能會引起內存泄漏

內存泄漏的解決方案

造成內存泄露的原因:
  • 意外的全局變量(在函數內部沒有使用var進行聲明的變量)
  • console.log
  • 閉包
  • 對象的循環引用
  • 未清除的計時器
  • DOM泄露(獲取到DOM節點之后,將DOM節點刪除,但是沒有手動釋放變量,拿對應的DOM節點在變量中還可以訪問到,就會造成泄露)
如何避免閉包引起的內存泄漏:
  1. 在退出函數之前,將不使用的局部變量全部刪除,可以使變量賦值為null
//這段代碼會導致內存泄露 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; //解除循環引用 el.onclick = function(){ alert(id); } el = null; // 將閉包引用的外部函數中活動對象清除 } 
  1. 避免變量的循環賦值和引用(代碼如上)
  2. 由於jQuery考慮到了內存泄漏的潛在危害,所以它會手動釋放自己指定的所有事件處理程序。只要堅持使用jQuery的事件綁定方法,就可以一定程度上避免這種特定的常見原因導致的內存泄漏。
//這段代碼會導致內存泄露 $(document).ready(function() { var button = document.getElementById('button-1'); button.onclick = function() { console.log('hello'); return false; }; }); //當指定單擊事件處理程序時,就創建了一個在其封閉的環境中包含button變量的閉包。而且,現在的button也包含一個指向閉包(onclick屬性自身)的引用。這樣,就導致了在IE中即使離開當前頁面也不會釋放這個循環。 //用jQuery化解引用循環 $(document).ready(function() { var $button = $('#button-1'); $button.click(function(event) { event.preventDefault(); console.log('hello'); }); }); 

閉包的使用場景

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

閉包實例--函數防抖

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

就是指觸發事件后在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。
通俗一點:在一段固定的時間內,只能觸發一次函數,在多次觸發事件時,只執行最后一次。

使用時機:
  • 搜索功能,在用戶輸入結束以后才開始發送搜索請求,可以使用函數防抖來實現;

閉包實例--函數節流

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

就是限制一個函數在一定時間內只能執行一次。

使用時機
  • 改變瀏覽器窗口尺寸,可以使用函數節流,避免函數不斷執行;
  • 滾動條scroll事件,通過函數節流,避免函數不斷執行。
函數節流與函數防抖的區別:

我們以一個案例來講一下它們之間的區別:
設定一個間隔時間為一秒,在一分鍾內,不斷的移動鼠標,讓它觸發一個函數,打印一些內容。

  • 函數防抖:會打印1次,在鼠標停止移動的一秒后打印。
  • 函數節流:會打印60次,因為在一分鍾內有60秒,每秒會觸發一次。
  • 總結:節流是為了限制函數的執行次數,而防抖是為了限制函數的執行時機。
函數節流與函數防抖的使用

此處使用一個對象的方法,主要為了測試this指向綁定的問題,調用的時候傳遞參數問題等。

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'); }; 

閉包實例--函數柯里化

閉包實例--給元素偽數組添加事件

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

閉包實例--不使用循環返回數組

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]




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM