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

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

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

前言

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

一、閉包與變量作用域

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

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

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

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

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

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

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

1.封裝私有變量

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

    function createUser(name, age) {
      return {
        getName: function () {
          return name; // 訪問(wèn)閉包中的變量
        },
        getAge: function () {
          return age; // 訪問(wèn)閉包中的變量
        },
        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

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

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

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

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

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

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

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

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

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

運(yùn)行過(guò)程總結(jié)

時(shí)間線:

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

輸出結(jié)果:

  • 在 1 秒后,控制臺(tái)會(huì)輸出 "任務(wù)取消"
  • 2 秒后,不會(huì)輸出 "執(zhí)行任務(wù)",因?yàn)槿蝿?wù)已被取消。

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

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

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

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

    function createCache() {
      const cache = {}; // 閉包中的緩存對(duì)象
      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")); // 輸出:小軍

在這個(gè)例子中,`createCache` 函數(shù)通過(guò)閉包封裝了一個(gè)緩存對(duì)象 `cache`。通過(guò) `set` 方法可以將數(shù)據(jù)存儲(chǔ)到緩存中,通過(guò) `get` 方法可以從緩存中獲取數(shù)據(jù)。這種緩存功能在實(shí)際開發(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)建一個(gè)可取消的函數(shù)執(zhí)行器
     * @param {Function} fn - 需要延遲執(zhí)行的函數(shù)
     * @param {Array} args - 傳遞給 fn 的參數(shù)數(shù)組
     * @param {number} t - 延遲時(shí)間(毫秒)
     * @return {Function} - 返回一個(gè)取消函數(shù),用于取消延遲執(zhí)行
     */
    var cancellable = function (fn, args, t) {
      let timeOutId; // 用于存儲(chǔ) setTimeout 返回的定時(shí)器 ID
      // 取消函數(shù),用于清除定時(shí)器
      function cancelFn() {
        clearTimeout(timeOutId); // 清除定時(shí)器,阻止 fn 執(zhí)行
      }
      // 設(shè)置定時(shí)器,延遲 t 毫秒后執(zhí)行 fn
      timeOutId = setTimeout(() => {
        fn(...args); // 使用展開運(yùn)算符將 args 作為參數(shù)傳遞給 fn
      }, t);
      // 返回取消函數(shù),供外部調(diào)用
      return cancelFn;
    };
    // 用于存儲(chǔ)執(zhí)行結(jié)果的數(shù)組
    const result = [];
    // 示例函數(shù),將輸入?yún)?shù)乘以 5
    const fn = (x) => x * 5;
    // 示例函數(shù)的參數(shù)和延遲時(shí)間
    const args = [2], t = 20, cancelTimeMs = 50;
    // 記錄開始時(shí)間,用于計(jì)算延遲執(zhí)行的時(shí)間差
    const start = performance.now();
    // 日志函數(shù),記錄函數(shù)執(zhí)行的時(shí)間和返回值
    const log = (...argsArr) => {
      const diff = Math.floor(performance.now() - start); // 計(jì)算從開始到現(xiàn)在的毫秒數(shù)
      result.push({ "time": diff, "returned": fn(...argsArr) }); // 將執(zhí)行時(shí)間和返回值存入 result
    };
    // 創(chuàng)建一個(gè)可取消的延遲任務(wù)
    const cancel = cancellable(log, args, t);
    // 在 cancelTimeMs 毫秒后調(diào)用取消函數(shù),取消延遲任務(wù)
    const maxT = Math.max(t, cancelTimeMs); // 計(jì)算延遲時(shí)間和取消時(shí)間的最大值
    setTimeout(cancel, cancelTimeMs);
    // 在延遲任務(wù)和取消任務(wù)之后,打印結(jié)果
    setTimeout(() => {
      console.log(result); // [{"time":20,"returned":10}]
    }, maxT + 15); // 確保在所有任務(wù)完成后打印結(jié)果
  </script>
</body>
</html>

詳細(xì)解釋:

設(shè)置定時(shí)器:

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

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

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

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

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

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

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

返回取消函數(shù):

return cancelFn;

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

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

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

. setTimeoutclearTimeout 的工作機(jī)制

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

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

四、閉包的注意事項(xiàng)

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

五、總結(jié)

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

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

相關(guān)文章

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

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

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

    js調(diào)用設(shè)備攝像頭的方法

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

    JS獲取幾種URL地址的方法小結(jié)

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

    詳談js原型繼承的一些問(wèn)題

    下面小編就為大家?guī)?lái)一篇詳談js原型繼承的一些問(wèn)題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • 微信小程序開發(fā)(一) 微信登錄流程詳解

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

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

    解析javascript中鼠標(biāo)滾輪事件

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

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

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

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

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

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

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

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

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

最新評(píng)論