亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript閉包實現(xiàn)函數(shù)返回函數(shù)詳解

 更新時間:2025年05月01日 09:49:20   作者:友人.227  
在JavaScript中,閉包是一個非常強大的特性,它允許一個函數(shù)訪問并操作函數(shù)外部的變量,閉包通常通過返回一個內(nèi)部函數(shù)來實現(xiàn),這使得外部函數(shù)可以保持某些變量的狀態(tài),即使外部函數(shù)已經(jīng)執(zhí)行完畢,這種技術(shù)常用于創(chuàng)建私有變量、封裝模塊、模擬私有方法等場景

前言

在JavaScript的世界里,閉包是一個既神秘又強大的特性。它既是JavaScript的一大難點,也是JavaScript的特色之一。閉包的運用貫穿于許多高級應(yīng)用中,可以說,掌握閉包,就能解鎖JavaScript編程的更多可能性。

一、閉包與變量作用域

要理解閉包,我們首先得從JavaScript的變量作用域講起。變量的作用域主要分為兩種:全局變量和局部變量。全局變量可以在代碼的任何地方被訪問,而局部變量則只能在其所在的函數(shù)內(nèi)部被訪問。JavaScript語言的特別之處在于,函數(shù)內(nèi)部可以直接讀取全局變量,但函數(shù)外部卻無法讀取函數(shù)內(nèi)部的局部變量。閉包就可以實現(xiàn)。

二、閉包的實現(xiàn)-函數(shù)返回函數(shù)

閉包通常是通過函數(shù)返回函數(shù)的方式來實現(xiàn)的。這種模式在JavaScript中非常常見,它不僅增強了代碼的靈活性,還為閉包的實現(xiàn)提供了便利。以下是一個簡單的例子,幫助你更好地理解閉包的實現(xiàn)方式:

    //  定義一個函數(shù)用來打招呼
    function greet(name) {
      return function () {
        console.log(`Hello, ${name}!`);
      };
    }
    // 定義一個變量 接收 返回值 
    // 這里返回的是一個函數(shù) 
    const greet1 = greet("小紅");
    console.log(greet1)
    /**
     * ? () {
        console.log(`Hello, ${name}!`);
        }
      */
    //  執(zhí)行 函數(shù) 
    greet1(); // 輸出:Hello, 小紅! 

在這個例子中,`greet` 函數(shù)接收一個參數(shù) `name`,并返回一個匿名函數(shù)。這個匿名函數(shù)在執(zhí)行時,會訪問其創(chuàng)建時所在的作用域鏈中的變量 `name`。當我們調(diào)用 `greet("小紅")` 時,返回的匿名函數(shù)就“捕獲”了變量 `name` 的值 `"Alice"`,并將其保存在閉包中。因此,當我們調(diào)用 `greet()` 時,它就會輸出 `"Hello, 小紅!"`。這就是閉包的神奇之處,它讓函數(shù)能夠記住并訪問其創(chuàng)建時的變量。

三、閉包的應(yīng)用場景

閉包的應(yīng)用場景非常廣泛,從簡單的問候函數(shù)到復(fù)雜的事件監(jiān)聽器、延遲任務(wù)和緩存功能,都可以看到閉包的身影。以下是一些常見的應(yīng)用場景:

1.封裝私有變量

閉包可以用來封裝私有變量,實現(xiàn)數(shù)據(jù)的封裝和隱藏。例如,我們可以創(chuàng)建一個用戶對象,通過閉包來封裝用戶的姓名和年齡:

    function createUser(name, age) {
      return {
        getName: function () {
          return name; // 訪問閉包中的變量
        },
        getAge: function () {
          return age; // 訪問閉包中的變量
        },
        setAge: function (newAge) {
          age = newAge; // 修改閉包中的變量
        }
      };
    }
    const user = createUser("小明", 25);
    console.log(user.getName()); // 輸出:小明
    console.log(user.getAge()); // 輸出:25
    user.setAge(26);
    console.log(user.getAge()); // 輸出:26

在這個例子中,`createUser` 函數(shù)通過閉包封裝了用戶的姓名和年齡。外部代碼無法直接訪問這些私有變量,只能通過 `getName`、`getAge` 和 `setAge` 方法來獲取和修改它們的值。這種封裝方式不僅保證了數(shù)據(jù)的安全性,還提供了靈活的接口供外部代碼使用。

2.創(chuàng)建獨立的計數(shù)器

閉包可以用來創(chuàng)建獨立的計數(shù)器,每個計數(shù)器都有自己的狀態(tài),互不影響。例如:

 function createCounter() {
      let count = 0; // 閉包中的狀態(tài)值
      return function () {
        count += 1; // 每次調(diào)用時遞增
        console.log(`Count: ${count}`);
      };
    }
    const counter1 = createCounter();
    counter1(); // 輸出:Count: 1
    counter1(); // 輸出:Count: 2
    counter2(); // 輸出:Count: 1
    counter2(); // 輸出:Count: 2

在這個例子中,每次調(diào)用 `createCounter` 函數(shù)時,都會創(chuàng)建一個新的閉包,其中包含一個獨立的計數(shù)器狀態(tài) `count`。因此,`counter1` 和 `counter2` 是兩個獨立的計數(shù)器,它們的計數(shù)互不影響。

3.實現(xiàn)延遲任務(wù)

閉包還可以用來實現(xiàn)延遲任務(wù),例如:

  // task:這是一個函數(shù),表示需要延遲執(zhí)行的任務(wù)。
    // delay:這是一個數(shù)字,表示延遲的時間(單位為毫秒)。
    function createDelayedTask(task, delay) {
      // 在函數(shù)內(nèi)部,定義了一個變量 timeoutId,用于存儲 setTimeout 返回的定時器 ID。
      // 這個變量被閉包捕獲,因此可以在返回的對象方法中訪問和修改它。
      let timeoutId;
      return {
        run: function () {
          timeoutId = setTimeout(task, delay);
        },
        cancel: function () {
          clearTimeout(timeoutId);
          console.log("任務(wù)取消");
        }
      };
    }
    // createDelayedTask 返回一個對象,包含 run 和 cancel 方法,
    // 并將其賦值給變量 delayedTask。
    // task:一個匿名箭頭函數(shù) () => console.log("執(zhí)行任務(wù)"),表示需要延遲執(zhí)行的任務(wù)。
    // delay:延遲時間為 2000 毫秒(即 2 秒)。
    const delayedTask = createDelayedTask(() => console.log("執(zhí)行任務(wù)"), 2000);
    // 在內(nèi)部,setTimeout 被調(diào)用,將傳入的任務(wù)函數(shù) () => console.log("執(zhí)行任務(wù)") 設(shè)置為在 2 秒后執(zhí)行。
    delayedTask.run(); // 啟動任務(wù)
    /**    這里又調(diào)用了一個 setTimeout,將 delayedTask.cancel 方法設(shè)置為在 1 秒后執(zhí)行。
    當 delayedTask.cancel 被調(diào)用時:
    clearTimeout(timeoutId) 被執(zhí)行,清除之前設(shè)置的定時器(即取消延遲任務(wù))。
    打印消息 "任務(wù)取消"。*/
    setTimeout(delayedTask.cancel, 1000); // 在 1 秒后取消任務(wù)

在這個例子中,`createDelayedTask` 函數(shù)通過閉包封裝了延遲任務(wù)的邏輯和狀態(tài)。`run` 方法用于啟動任務(wù),`cancel` 方法用于取消任務(wù)。通過閉包,我們可以將任務(wù)的狀態(tài)(如 `timeoutId`)保存起來,方便在需要時進行操作。

運行過程總結(jié)

時間線:

  • 0 秒:調(diào)用 delayedTask.run(),設(shè)置一個定時器,計劃在 2 秒后執(zhí)行任務(wù)(打印 "執(zhí)行任務(wù)")。
  • 1 秒:調(diào)用 delayedTask.cancel(),清除定時器,取消任務(wù)。
  • 2 秒:原計劃的任務(wù)不會執(zhí)行,因為定時器已被清除。

輸出結(jié)果:

  • 在 1 秒后,控制臺會輸出 "任務(wù)取消"。
  • 2 秒后,不會輸出 "執(zhí)行任務(wù)",因為任務(wù)已被取消。

這種模式在實際開發(fā)中非常有用,例如:

  • 在用戶操作頻繁的場景下,避免重復(fù)觸發(fā)某些操作(如搜索框的防抖功能)。
  • 在需要延遲執(zhí)行任務(wù)但可能需要取消任務(wù)的場景中(如用戶取消操作或超時取消任務(wù))。

4.實現(xiàn)緩存功能

閉包還可以用來實現(xiàn)緩存功能,例如:

    function createCache() {
      const cache = {}; // 閉包中的緩存對象
      return {
        get: function (key) {
          return cache[key];
        },
        set: function (key, value) {
          cache[key] = value;
        }
      };
    }
    const myCache = createCache();
    myCache.set("name", "小軍");
    console.log(myCache.get("name")); // 輸出:小軍

在這個例子中,`createCache` 函數(shù)通過閉包封裝了一個緩存對象 `cache`。通過 `set` 方法可以將數(shù)據(jù)存儲到緩存中,通過 `get` 方法可以從緩存中獲取數(shù)據(jù)。這種緩存功能在實際開發(fā)中非常有用,可以提高程序的性能。

5.leetcode.2715執(zhí)行可取消函數(shù)的解法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ArrayWrapper Example</title>
</head>
<body>
  <script>
    /**
     * 創(chuàng)建一個可取消的函數(shù)執(zhí)行器
     * @param {Function} fn - 需要延遲執(zhí)行的函數(shù)
     * @param {Array} args - 傳遞給 fn 的參數(shù)數(shù)組
     * @param {number} t - 延遲時間(毫秒)
     * @return {Function} - 返回一個取消函數(shù),用于取消延遲執(zhí)行
     */
    var cancellable = function (fn, args, t) {
      let timeOutId; // 用于存儲 setTimeout 返回的定時器 ID
      // 取消函數(shù),用于清除定時器
      function cancelFn() {
        clearTimeout(timeOutId); // 清除定時器,阻止 fn 執(zhí)行
      }
      // 設(shè)置定時器,延遲 t 毫秒后執(zhí)行 fn
      timeOutId = setTimeout(() => {
        fn(...args); // 使用展開運算符將 args 作為參數(shù)傳遞給 fn
      }, t);
      // 返回取消函數(shù),供外部調(diào)用
      return cancelFn;
    };
    // 用于存儲執(zhí)行結(jié)果的數(shù)組
    const result = [];
    // 示例函數(shù),將輸入?yún)?shù)乘以 5
    const fn = (x) => x * 5;
    // 示例函數(shù)的參數(shù)和延遲時間
    const args = [2], t = 20, cancelTimeMs = 50;
    // 記錄開始時間,用于計算延遲執(zhí)行的時間差
    const start = performance.now();
    // 日志函數(shù),記錄函數(shù)執(zhí)行的時間和返回值
    const log = (...argsArr) => {
      const diff = Math.floor(performance.now() - start); // 計算從開始到現(xiàn)在的毫秒數(shù)
      result.push({ "time": diff, "returned": fn(...argsArr) }); // 將執(zhí)行時間和返回值存入 result
    };
    // 創(chuàng)建一個可取消的延遲任務(wù)
    const cancel = cancellable(log, args, t);
    // 在 cancelTimeMs 毫秒后調(diào)用取消函數(shù),取消延遲任務(wù)
    const maxT = Math.max(t, cancelTimeMs); // 計算延遲時間和取消時間的最大值
    setTimeout(cancel, cancelTimeMs);
    // 在延遲任務(wù)和取消任務(wù)之后,打印結(jié)果
    setTimeout(() => {
      console.log(result); // [{"time":20,"returned":10}]
    }, maxT + 15); // 確保在所有任務(wù)完成后打印結(jié)果
  </script>
</body>
</html>

詳細解釋:

設(shè)置定時器:

timeoutId = setTimeout(() => {
    fn(...args); // 使用 args 作為參數(shù)執(zhí)行 fn
}, t);

這里使用 setTimeout 設(shè)置了一個定時器,延遲 t 毫秒后執(zhí)行 fn(...args)。

setTimeout 返回一個唯一的 timeoutId,這個 ID 用于后續(xù)的取消操作。

2.定義取消函數(shù):

function cancelFn() {
    clearTimeout(timeoutId); // 清除定時器,取消 fn 的執(zhí)行
}

cancelFn 是一個函數(shù),它的作用是調(diào)用 clearTimeout(timeoutId)。

如果在 fn 執(zhí)行之前調(diào)用了 cancelFn,clearTimeout 會取消對應(yīng)的定時器,fn 就不會被執(zhí)行。

返回取消函數(shù):

return cancelFn;

返回 cancelFn 是為了讓調(diào)用者能夠在需要的時候調(diào)用它。

如果調(diào)用者沒有調(diào)用 cancelFn,定時器會正常觸發(fā),fn 會在延遲時間 t 后執(zhí)行。

如果調(diào)用者調(diào)用了 cancelFn,clearTimeout 會取消定時器,fn 就不會被執(zhí)行。

. setTimeoutclearTimeout 的工作機制

setTimeout:設(shè)置一個定時器,延遲 t 毫秒后執(zhí)行某個函數(shù)。它返回一個定時器的 ID(timeoutId),這個 ID 用于后續(xù)的取消操作。

clearTimeout:通過傳入定時器的 ID 來取消對應(yīng)的定時器。如果定時器已經(jīng)被觸發(fā)(即回調(diào)函數(shù)已經(jīng)開始執(zhí)行),clearTimeout 將不會有任何效果。

四、閉包的注意事項

雖然閉包非常強大,但在使用時也需要小心一些潛在的問題。例如,閉包可能會導(dǎo)致內(nèi)存泄漏,因為閉包會一直保存其創(chuàng)建時的作用域鏈中的變量,即使這些變量不再被使用,也不會被垃圾回收器回收。因此,在使用閉包時,我們需要確保及時釋放不再使用的變量,避免內(nèi)存泄漏。

五、總結(jié)

閉包是JavaScript中一個非常重要的特性,它通過函數(shù)返回函數(shù)的方式,讓函數(shù)能夠記住并訪問其創(chuàng)建時所在的作用域鏈中的變量。閉包不僅可以封裝私有變量,實現(xiàn)數(shù)據(jù)的封裝和隱藏,還可以創(chuàng)建獨立的計數(shù)器、實現(xiàn)延遲任務(wù)和緩存功能等。在實際開發(fā)中,閉包的應(yīng)用場景非常廣泛,掌握閉包的使用方法,可以讓你的代碼更加靈活和強大。當然,在使用閉包時,我們也要注意避免內(nèi)存泄漏等問題,合理地使用閉包,才能充分發(fā)揮其優(yōu)勢。

以上就是JavaScript閉包實現(xiàn)函數(shù)返回函數(shù)詳解的詳細內(nèi)容,更多關(guān)于JavaScript函數(shù)返回函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論