js閉包的6種應(yīng)用場(chǎng)景總結(jié)
什么是閉包?
閉包的基本定義
如果一個(gè)函數(shù)訪問(wèn)了此函數(shù)的父級(jí)及父級(jí)以上的作用域變量,那么這個(gè)函數(shù)就是一個(gè)閉包。閉包會(huì)創(chuàng)建一個(gè)包含外部函數(shù)作用域變量的環(huán)境,并將其保存在內(nèi)存中,這意味著,即使外部函數(shù)已經(jīng)執(zhí)行完畢,閉包仍然可以訪問(wèn)和使用外部函數(shù)的變量。
//閉包實(shí)例代碼 function fn1() { let a = 1; function fn2() { a++; console.log(a); } return fn2; } const fn2 = fn1(); //閉包函數(shù)執(zhí)行完后外部作用域變量仍然存在,并保持狀態(tài) fn2() //2 fn2() //3
閉包的優(yōu)缺點(diǎn)及特性
- 閉包的優(yōu)點(diǎn):
- 保護(hù)變量:閉包可以將變量封裝在函數(shù)內(nèi)部,避免全局污染,保護(hù)變量不被外部訪問(wèn)和修改。
- 延長(zhǎng)變量生命周期:閉包使得函數(shù)內(nèi)部的變量在函數(shù)執(zhí)行完后仍然存在,可以在函數(shù)外部繼續(xù)使用。
- 實(shí)現(xiàn)模塊化:閉包可以創(chuàng)建私有變量和私有方法,實(shí)現(xiàn)模塊化的封裝和隱藏,提高代碼的可維護(hù)性和安全性。
- 保持狀態(tài):閉包可以捕獲外部函數(shù)的變量,并在函數(shù)執(zhí)行時(shí)保持其狀態(tài)。這使得閉包在事件處理、回調(diào)函數(shù)等場(chǎng)景中非常有用。
- 閉包的缺點(diǎn):
- 內(nèi)存占用:閉包會(huì)導(dǎo)致外部函數(shù)的變量無(wú)法被垃圾回收,從而增加內(nèi)存占用。如果濫用閉包,會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題。
- 性能損耗:閉包涉及到作用域鏈的查找過(guò)程,會(huì)帶來(lái)一定的性能損耗。在性能要求高的場(chǎng)景下,需要注意閉包的使用。
- 閉包的特性:
- 函數(shù)嵌套:閉包的實(shí)現(xiàn)依賴于函數(shù)嵌套,即在一個(gè)函數(shù)內(nèi)部定義另一個(gè)函數(shù)。
- 記憶外部變量:閉包可以記住并訪問(wèn)外部函數(shù)的變量,即使外部函數(shù)已經(jīng)執(zhí)行完畢。
- 延長(zhǎng)作用域鏈:閉包將外部函數(shù)的作用域鏈延長(zhǎng)到內(nèi)部函數(shù)中,使得內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)的變量。
- 返回函數(shù):閉包通常以函數(shù)的形式返回,使得外部函數(shù)的變量仍然可以被內(nèi)部函數(shù)引用和使用。
閉包的應(yīng)用場(chǎng)景
自執(zhí)行函數(shù)
let say = (function(){ let val = 'hello world'; function say(){ console.log(val); } return say; })()
節(jié)流防抖
// 節(jié)流函數(shù)封裝 function throttle(func, delay) { let timer = null; return function () { if (!timer) { timer = setTimeout(() => { func.apply(this, arguments); timer = null; }, delay); } }; } // 防抖函數(shù)封裝 function debounce(func, delay) { let timer = null; return function () { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, arguments); }, delay); }; }
函數(shù)柯里化
- 函數(shù)柯里化(Currying)是一種將多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換為一系列接受單個(gè)參數(shù)的函數(shù)的過(guò)程。舉個(gè)簡(jiǎn)單的例子,我們有一個(gè)原始函數(shù)add(a, b, c),我們可以將它柯里化為addCurried(a)(b)(c)的形式。
//柯里化前 function add(a, b, c) { return a + b + c; } console.log(add(1, 2, 3)); //6 //柯里化后 function addCurried1(a) { return function (b) { return function (c) { return a + b + c; }; }; } //箭頭函數(shù)簡(jiǎn)寫 const addCurried2 = (a) => (b) => (c) => a + b + c; console.log(addCurried1(1)(2)(3)); //6 console.log(addCurried2(1)(2)(3)); //6
鏈?zhǔn)秸{(diào)用
- 利用閉包原理封裝一個(gè)簡(jiǎn)單的計(jì)算器
function calculator() { let result = 0; function add(num) { result += num; return this; } function subtract(num) { result -= num; return this; } function multiply(num) { result *= num; return this; } function divide(num) { result /= num; return this; } function getResult() { return result; } function clear() { result = 0; return this; } return { add, subtract, multiply, divide, getResult, clear, }; } const calc = calculator(); const result = calc.add(5).subtract(2).divide(3).multiply(6).getResult(); console.log(result); // 輸出:6
迭代器
function createIterator(arr) { let index = 0; return { next: function() { if (index < arr.length) { return { value: arr[index++], done: false }; } else { return { done: true }; } } }; } const myIterator = createIterator([1, 2, 3]); console.log(myIterator.next()); // { value: 1, done: false } console.log(myIterator.next()); // { value: 2, done: false } console.log(myIterator.next()); // { value: 3, done: false } console.log(myIterator.next()); // { done: true }
發(fā)布-訂閱模式
function createPubSub() { // 存儲(chǔ)事件及其對(duì)應(yīng)的訂閱者 const subscribers = {}; // 訂閱事件 function subscribe(event, callback) { // 如果事件不存在,則創(chuàng)建一個(gè)新的空數(shù)組 if (!subscribers[event]) { subscribers[event] = []; } // 將回調(diào)函數(shù)添加到訂閱者數(shù)組中 subscribers[event].push(callback); } // 發(fā)布事件 function publish(event, data) { // 如果事件不存在,則直接返回 if (!subscribers[event]) { return; } // 遍歷訂閱者數(shù)組,調(diào)用每個(gè)訂閱者的回調(diào)函數(shù) subscribers[event].forEach((callback) => { callback(data); }); } // 返回訂閱和發(fā)布函數(shù) return { subscribe, publish, }; } // 使用示例 const pubSub = createPubSub(); // 訂閱事件 pubSub.subscribe("event1", (data) => { console.log("訂閱者1收到事件1的數(shù)據(jù):", data); }); pubSub.subscribe("event2", (data) => { console.log("訂閱者2收到事件2的數(shù)據(jù):", data); }); // 發(fā)布事件 pubSub.publish("event1", "Hello"); // 輸出: 訂閱者1收到事件1的數(shù)據(jù): Hello pubSub.publish("event2", "World"); // 輸出: 訂閱者2收到事件2的數(shù)據(jù): World
閉包造成的內(nèi)存泄漏怎么解決呢?
閉包中的內(nèi)存泄漏指的是在閉包函數(shù)中,由于對(duì)外部變量的引用而導(dǎo)致這些變量無(wú)法被垃圾回收機(jī)制釋放的情況。當(dāng)一個(gè)函數(shù)內(nèi)部定義了一個(gè)閉包,并且這個(gè)閉包引用了外部變量時(shí),如果這個(gè)閉包被其他地方持有,就會(huì)導(dǎo)致外部變量無(wú)法被正常釋放,從而造成內(nèi)存泄漏。
解決閉包中的內(nèi)存泄漏問(wèn)題通常需要注意解除外部變量和閉包函數(shù)的引用,以及解綁函數(shù)本身的引用,使得閉包中引用的外部變量和閉包函數(shù)能夠被垃圾回收機(jī)制釋放。
- 以下是使用閉包時(shí)解決內(nèi)存泄漏的示例
function createClosure() { let value = 'Hello'; // 閉包函數(shù) var closure = function() { console.log(value); }; // 解綁定閉包函數(shù),并釋放資源 var releaseClosure = function() { value = null; // 解除外部變量的引用 closure = null; // 解除閉包函數(shù)的引用 releaseClosure = null; // 解除解綁函數(shù)的引用 }; // 返回閉包函數(shù)和解綁函數(shù) return { closure, releaseClosure }; } // 創(chuàng)建閉包 var closureObj = createClosure(); // 調(diào)用閉包函數(shù) closureObj.closure(); // 輸出:Hello // 解綁閉包并釋放資源 closureObj.releaseClosure(); // 嘗試調(diào)用閉包函數(shù),此時(shí)已解綁,不再引用外部變量 closureObj.closure(); // 輸出:null
以上就是js閉包的6種應(yīng)用場(chǎng)景總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于js閉包應(yīng)用場(chǎng)景的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript Sort 的一個(gè)錯(cuò)誤用法示例
這篇文章主要介紹了JavaScript Sort 的一個(gè)錯(cuò)誤用法示例,本文分析了一個(gè)Sort實(shí)例得到了這個(gè)錯(cuò)誤用法并給出了解決方法,需要的朋友可以參考下2015-03-03JavaScript動(dòng)態(tài)修改背景顏色的方法
這篇文章主要介紹了JavaScript動(dòng)態(tài)修改背景顏色的方法,涉及javascript操作鼠標(biāo)事件及樣式的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04js+canvas實(shí)現(xiàn)飛機(jī)大戰(zhàn)
這篇文章主要為大家詳細(xì)介紹了js?canvas實(shí)現(xiàn)飛機(jī)大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05JavaScript實(shí)現(xiàn)自動(dòng)彈出窗口并自動(dòng)關(guān)閉窗口的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)自動(dòng)彈出窗口并自動(dòng)關(guān)閉窗口的方法,可實(shí)現(xiàn)從頁(yè)面左側(cè)彈出窗口5秒后窗口向右移動(dòng)并消失的效果,涉及javascript針對(duì)頁(yè)面窗口及樣式的定義操作技巧,需要的朋友可以參考下2015-08-08javascript開發(fā)實(shí)現(xiàn)貪吃蛇游戲
這篇文章主要為大家詳細(xì)介紹了javascript開發(fā)實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07JavaScript判斷當(dāng)前時(shí)間是在某個(gè)時(shí)間點(diǎn)之前/之后
本文主要介紹了JavaScript判斷當(dāng)前時(shí)間是在某個(gè)時(shí)間點(diǎn)之前/之后,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07AngularJS+Bootstrap實(shí)現(xiàn)多文件上傳與管理
這篇文章主要為大家詳細(xì)介紹了AngularJS+Bootstrap實(shí)現(xiàn)多文件上傳與管理,對(duì)上傳文件進(jìn)行加載與刪除操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11javascript實(shí)現(xiàn)的使用方向鍵控制光標(biāo)在table單元格中切換
最近公司開發(fā)ERP項(xiàng)目,要求商品入庫(kù)選擇貨架號(hào)時(shí)支持使用方向鍵快速選擇,以提高入庫(kù)效率。2010-11-11js實(shí)現(xiàn)跨域的4種實(shí)用方法原理分析
這篇文章主要分析了js實(shí)現(xiàn)跨域的4種實(shí)用方法原理,主要是使用jsonp跨域,使用window.name來(lái)進(jìn)行跨域,對(duì)這方面感興趣的朋友可以參考一下2015-10-10