1. 程式人生 > 其它 >js閉包原理及使用

js閉包原理及使用

技術標籤:jsjsvuejavascripthtml5html

保持對程式碼的熱愛並保持懷疑態度
昨天寫了一篇點選檢視js防抖和節流的理解用到了閉包,剛剛好我也忘的差不多了,只知道閉包長什麼,原理什麼的早就忘到九霄雲外了,所以準備重新梳理了一下閉包。

閉包的概念及原理

閉包是指有權訪問另外一個函式作用域中的變數的函式。
也可以這麼說,這樣可能更能理解,也就是閉包的原理
通過作用域的巢狀,觸發計算機的垃圾回收機制(硬碟),將原本的區域性變數進化成私有變數的環境,叫閉包

計算機的垃圾回收機制

  • 硬碟:需要刪除的資料,不直接刪除,而是再是儲存在一個臨時區域,如果需要繼續使用,可以從臨時區域取出

  • 記憶體:需要刪除的資料,直接刪除,不可恢復

  • 程式,或程式碼的執行,預設使用的是記憶體的垃圾回收,閉包使用的是硬碟的垃圾回收

例項

首先來看這麼一個需求, 要求多次執行a的值累加,不允許出現全域性變數

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

如果這麼寫的話,a只會輸出11,因為函式每次都是重新執行。

但是這麼寫呢

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

很顯然是可以實現累加的,沒錯這就是閉包
在這裡插入圖片描述

例項分析

在分析上訴程式碼中,首先得知道這麼幾個知識

關於函式的定義作用域和執行作用域

  • 定義函式時的作用域,叫定義作用域
  • 執行函式時的作用域,叫執行作用域
  • 無論函式在哪執行,都可以在自身內部使用自身定義作用域中的資料

拿上面的例子來分析

    function fn() {     定義
        var a = 10;      這個a相對於下面一個函式相當於是父級
        return function(){    定義返回值
            a++;       每次都累加a,這裡也沒有宣告a,所以會直接會改變a的值,執行一次就改變一次,所以就實現了累加。
            console.log(a);
        }
    }
    var f=fn();  賦值
    f();   執行 ,函式在執行時,可以拿到自身的定義作用域的變數
    f();    
    f();

也可以這麼寫,通過傳參的方式

    function fn(a) {
        此時的a是fn的形參。通過作用域的巢狀,a此時相當於返回函式的父級作用域  ,也可以實現累加
        return function(){      
            a++;
            console.log(a);
        }
    }
    var f=fn(10); 
    f();
    f();
    f();

閉包的特點

  • 可以在作用域外部修改作用域內部的資料
  • 連線了作用域內外部的橋樑
  • 消耗記憶體
  • 記憶體洩漏(記憶體溢位)(低版本瀏覽器以下)

閉包的應用

可以給內建的方法的回撥函式傳參

看例項

    setTimeout(fn("hello"), 1000);
    function fn(a){
        console.log(a);
    }

如果直接這麼寫的話,不會等到1秒鐘以後再console.log,而是立即執行,為什麼呢,涉及到了巨集任務和微任務,點選檢視非同步,同步,巨集任務,微任務理解但這不是重點,重點我們用閉包來解決這個問題

 setTimeout(fn("hello"), 1000);
    function fn(a){
        return function(){
            console.log(a);
        }
    }

這裡就是通過傳參的方式來解決的。

迴圈中的事件內使用迴圈的變數

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        
    </style>
</head>
<body>
    <ul>
        <li>link1</li>
        <li>link2</li>
        <li>link3</li>
        <li>link4</li>
        <li>link5</li>
    </ul>
</body>
<script>

    // 迴圈中的事件內使用迴圈的變數
    var ali = document.querySelectorAll("li")

    for(var i=0;i<ali.length;i++){
        (function(i){
            ali[i].onclick = function(){
                console.log(i);
            }
        })(i);
    }

    for(var i=0;i<ali.length;i++){
        ali[i].onclick = (function(i){
            return function(){
                console.log(i);
            }
        })(i);
    }

    for(let i=0;i<ali.length;i++){
        ali[i].onclick = function(){
            console.log(i);
        }
    }



</script>
</html>

以上的三種方式均可以獲取到li的索引。