1. 程式人生 > 實用技巧 >javascript之閉包

javascript之閉包

什麼是閉包

含義:是指有權訪問另一個函式作用域中的變數

優點:可以重複使用,並且不會造成變數汙染。(全域性變數可以重複使用,但是容易造成變數汙染。區域性變數僅在區域性作用域內有效,不可以重複使用,不會造成變數汙染。閉包則結合了全域性變數和區域性變數的優點)

缺點:比普通函式更佔用記憶體,函式呼叫後不會銷燬變數容易導致網頁效能變差,在IE下容易造成記憶體洩漏

可能造成記憶體洩漏的原因:

1、意外的全域性變數(在函式內部沒有使用var進行宣告的變數)

2、console.log

3、閉包

4、物件的迴圈引用

5、未清除的計時器

6、DOM洩露(獲取到DOM節點之後,將DOM節點刪除,但是沒有手動釋放變數,拿對應的DOM節點在變數中還可以訪問到,就會造成洩漏)

閉包造成記憶體洩漏的原因:

因為閉包就是能夠訪問外部函式變數的一個函式,而函式必須是儲存在記憶體中的物件,所以位於函式執行上下文中的所有變數也需要儲存在記憶體中,這樣就不會被回收,如果一旦迴圈引用或建立閉包,就會佔據大量記憶體,可能會引起記憶體洩漏。

如何避免閉包引起的記憶體洩漏:

1、將閉包引用的外部函式中活動物件清除(引用變數清除)

2、避免變數的迴圈賦值和引用

閉包應用

1、函式防抖:就是指觸發事件後在秒內函式只能執行一次,如果在n秒內又觸發了事件,則會重新計算函式執行事件(通俗的說:在一段固定的時間內,只能觸發一次函式,在多次觸發事件時,只執行最後一次)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>函式防抖</title>
</head>
<body>
    <div id="id" style="width:40px;height:40px;line-height:40px;border:1px solid #ddd;text-align:center">2</div>
    <script>
    //
======閉包例項:函式防抖===== var num = 2; var el = document.getElementById('id'); console.log(el,'el') function count() { el.innerText = ++num; } count() /** * @function debounce 函式防抖 * @param {Function} fn 需要防抖的函式 * @param {Number} interval 間隔時間 * @return {Function} 經過防抖處理的函式
*/ function debounce(func, wait) { let timer; return function() { let _this = this; let args = arguments; if(timer) clearTimeout(timer); timer = setTimeout(()=> { func.apply(_this,args); },wait); } } el.onmousemove = debounce(count,1000); </script> </body> </html>

2、函式節流:就是限制一個函式在一定時間內只能執行一次

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>函式節流</title>
</head>
<body>
    <div id="id" style="width: 80px;height: 40px;line-height:40px;text-align: center;border: 1px solid #ddd;cursor:pointer;font-size: 12px;">2</div>
    <script>
        /** 閉包例項:函式節流 */
        let el = document.getElementById('id');
        let num = 2;
        function count() {
            el.innerHTML = ++num;
            console.log(num,'num')
        }
        /**
        * @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) {
                    fn.apply(_this, args);
                    firstTime = null;
                }if(timer) return;
                timer = setTimeout(() => {
                    fn.apply(_this, args);
                    timer = null;
                },interval || 300)
                console.log(timer,'timer')
            }
        }
        el.onmousemove = throttle(count, 1000);
    </script>
</body>
</html>

函式防抖與函式節流的區別:

案例:設定一個間隔時間為1秒,在一分鐘內,不斷移動滑鼠,讓它觸發一個函式(觸發次數遠遠大於60次),列印一些內容。

結果:

函式防抖:會列印一次,在滑鼠停止移動的一秒後列印。

函式節流:會列印60次,因為在一分鐘內有60秒,每秒會且僅會觸發一次。

總結:節流是為了限制函式的執行次數,而防抖是為了限制函式執行時機(只執行最後一次)

3、其他應用:

/**
 * 不使用迴圈返回陣列
 */
  function getNewArr() {
      let n = 6;
      let newArr = [];
      return (function() {
                newArr.unshift(n);
                n--;
                console.log(newArr,'陣列',n)
                if(n>0) {
                    arguments.callee();//遞迴呼叫本函式
                }
                return newArr
        })()
  }
  getNewArr()