欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

一、閉包與變量作用域

要理解閉包,我們首先得從JavaScript的變量作用域講起。變量的作用域主要分為兩種:全局變量和局部變量。全局變量可以在代碼的任何地方被訪問,而局部變量則只能在其所在的函數(shù)內部被訪問。JavaScript語言的特別之處在于,函數(shù)內部可以直接讀取全局變量,但函數(shù)外部卻無法讀取函數(shù)內部的局部變量。閉包就可以實現(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`。當我們調用 `greet("小紅")` 時,返回的匿名函數(shù)就“捕獲”了變量 `name` 的值 `"Alice"`,并將其保存在閉包中。因此,當我們調用 `greet()` 時,它就會輸出 `"Hello, 小紅!"`。這就是閉包的神奇之處,它讓函數(shù)能夠記住并訪問其創(chuàng)建時的變量。

三、閉包的應用場景

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

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; // 每次調用時遞增
        console.log(`Count: ${count}`);
      };
    }
    const counter1 = createCounter();
    counter1(); // 輸出:Count: 1
    counter1(); // 輸出:Count: 2
    counter2(); // 輸出:Count: 1
    counter2(); // 輸出:Count: 2

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

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

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

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

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

運行過程總結

時間線:

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

輸出結果:

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

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

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

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í)行
      }
      // 設置定時器,延遲 t 毫秒后執(zhí)行 fn
      timeOutId = setTimeout(() => {
        fn(...args); // 使用展開運算符將 args 作為參數(shù)傳遞給 fn
      }, t);
      // 返回取消函數(shù),供外部調用
      return cancelFn;
    };
    // 用于存儲執(zhí)行結果的數(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)建一個可取消的延遲任務
    const cancel = cancellable(log, args, t);
    // 在 cancelTimeMs 毫秒后調用取消函數(shù),取消延遲任務
    const maxT = Math.max(t, cancelTimeMs); // 計算延遲時間和取消時間的最大值
    setTimeout(cancel, cancelTimeMs);
    // 在延遲任務和取消任務之后,打印結果
    setTimeout(() => {
      console.log(result); // [{"time":20,"returned":10}]
    }, maxT + 15); // 確保在所有任務完成后打印結果
  </script>
</body>
</html>

詳細解釋:

設置定時器:

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

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

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

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

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

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

如果在 fn 執(zhí)行之前調用了 cancelFnclearTimeout 會取消對應的定時器,fn 就不會被執(zhí)行。

返回取消函數(shù):

return cancelFn;

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

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

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

. setTimeoutclearTimeout 的工作機制

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

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

四、閉包的注意事項

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

五、總結

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

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

相關文章

  • js實現(xiàn)base64、url和blob之間相互轉換的三種方式

    js實現(xiàn)base64、url和blob之間相互轉換的三種方式

    Blob對象表示一個不可變、原始數(shù)據(jù)的類文件對象,Blob表示的不一定是JavaScript原生格式的數(shù)據(jù),下面這篇文章主要給大家介紹了關于js實現(xiàn)base64、url和blob之間相互轉換的三種方式,需要的朋友可以參考下
    2023-04-04
  • js調用設備攝像頭的方法

    js調用設備攝像頭的方法

    這篇文章主要為大家詳細介紹了js調用設備攝像頭的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • JS獲取幾種URL地址的方法小結

    JS獲取幾種URL地址的方法小結

    本篇文章主要是對JS獲取幾種URL地址的方法進行了總結介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-02-02
  • 詳談js原型繼承的一些問題

    詳談js原型繼承的一些問題

    下面小編就為大家?guī)硪黄斦刯s原型繼承的一些問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • 微信小程序開發(fā)(一) 微信登錄流程詳解

    微信小程序開發(fā)(一) 微信登錄流程詳解

    本篇文章主要介紹了微信小程序開發(fā)(一) 微信登錄流程,非常具有實用價值,需要的朋友可以參考下。
    2017-01-01
  • 解析javascript中鼠標滾輪事件

    解析javascript中鼠標滾輪事件

    這篇文章主要給大家詳細介紹了javascript中鼠標滾輪事件,圖文并茂,十分的詳細,有需要的小伙伴可以參考下。
    2015-05-05
  • layui實現(xiàn)把數(shù)據(jù)表格時間戳轉換為時間格式的例子

    layui實現(xiàn)把數(shù)據(jù)表格時間戳轉換為時間格式的例子

    今天小編就為大家分享一篇layui實現(xiàn)把數(shù)據(jù)表格時間戳轉換為時間格式的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • JavaScript基礎系列之函數(shù)和方法詳解

    JavaScript基礎系列之函數(shù)和方法詳解

    經(jīng)常談論起函數(shù)和方法,也常常搞不清楚它們之間的界限,經(jīng)常把兩個混用,這篇文章主要給大家介紹了關于JavaScript基礎系列之函數(shù)和方法的相關資料,需要的朋友可以參考下
    2021-09-09
  • js 彈出框只彈一次(二次修改之后的)

    js 彈出框只彈一次(二次修改之后的)

    彈出框只彈一次,看到網(wǎng)上也就寫的很多,可以直接使用的沒有幾個,下面與大家分享個修改之后的代碼,需要的朋友可以參考下
    2013-11-11
  • js中利用cookie實現(xiàn)記住密碼功能

    js中利用cookie實現(xiàn)記住密碼功能

    這篇文章主要為大家詳細介紹了js中利用cookie實現(xiàn)記住密碼功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10

最新評論