js中的函數(shù)嵌套和閉包詳情
前言:
今天就先和大家一起聊一聊我理解的閉包。在聊這個(gè)問(wèn)題之前,先了解一下變量的定義域。
在js中,變量定義域有全局作用域和局部作用域之說(shuō)。es6
中新出現(xiàn)的變量聲明關(guān)鍵字,就是為了解決部分變量作用域混亂引入的。全局作用域在這就不談了。主要說(shuō)說(shuō)函數(shù)的作用域。
一、作用域
簡(jiǎn)單一點(diǎn)說(shuō),函數(shù)的作用域,就是函數(shù)的花括號(hào)內(nèi)部,先看兩個(gè)例子,或許能對(duì)這個(gè)概念理解的更好一點(diǎn)
function f1(){ let n = 999 console.log(n) } f1() // 999 function f2(){ let n = 999 } alert(n); // 報(bào)錯(cuò)
二、函數(shù)的返回值
要說(shuō)閉包之前,我得先說(shuō)一下函數(shù)返回值。關(guān)于函數(shù)的返回值,小編也是年初才有了一個(gè)更深層次的理解。沒(méi)有返回值的函數(shù),執(zhí)行之后會(huì)返回undefined
,有返回值的函數(shù),執(zhí)行之后就變成了對(duì)應(yīng)的返回值。就像這樣
// 沒(méi)有返回值的函數(shù) function f1(){ alert(666) } console.log(f1()) // 出現(xiàn)彈窗之后,在控制臺(tái)輸出undefind // 存在返回值 function f2(){ alert(666) return 'over' } console.log(f2()) // 出現(xiàn)彈窗之后,在控制臺(tái)輸出over。當(dāng)然,可以返回字符串,也可以返回Bealon,還可以返回函數(shù)。
三、函數(shù)嵌套
在《重構(gòu)——改善既有代碼的設(shè)計(jì)》中,提出了js語(yǔ)法是允許函數(shù)內(nèi)部嵌套函數(shù)的,但并不是所有的編程語(yǔ)言都可以的,所謂代碼嵌套,就是在函數(shù)內(nèi)部又有函數(shù)聲明,
就像這樣:
function outer(){ let name = 'lilei' function inner(){ console.log(name) } }
四、閉包
前面說(shuō)明了在js中的局部變量作用域的問(wèn)題,在實(shí)際項(xiàng)目中,就是需要在函數(shù)外部,訪問(wèn)函數(shù)內(nèi)部的變量,這個(gè)時(shí)候,按照局部變量作用域的問(wèn)題。似乎是不可能的,閉包的出現(xiàn),解決了這個(gè)問(wèn)題。
function outer(){ let name = 'lilei' function inner(){ return name } return inner }
上面是一個(gè)典型的閉包函數(shù),在使用這個(gè)閉包函數(shù)的時(shí)候,我們可以這樣:
let g = outer() console.log(g()) // lilei
至此,已經(jīng)解決了在全局訪問(wèn)函數(shù)內(nèi)的局部變量。但是小編在回家的路上在想,為了實(shí)現(xiàn)這個(gè)功能,是不是不用這個(gè)麻煩,我通過(guò)這樣的函數(shù),也是可以滿足需求的。
function outer(){ let name = 'lilei' return name } console.log(outer()) // lilei
確實(shí)上面的代碼和通過(guò)閉包最終在控制臺(tái)輸出的內(nèi)容是一樣的,那為什么還要引入閉包呢?小編也是想了接近一周才明白的,這就好比變量->函數(shù)->類(lèi),每層往上都是逐步提升的過(guò)程,通過(guò)函數(shù)可以實(shí)現(xiàn)更多的邏輯,比如對(duì)數(shù)據(jù)進(jìn)行處理,僅僅靠變量是不能實(shí)現(xiàn)的。
五、閉包的實(shí)際應(yīng)用
上面小編介紹了閉包,那么在實(shí)際項(xiàng)目中有什么應(yīng)用呢?先看下面代碼:
1、隱藏內(nèi)部變量名稱(chēng)和函數(shù)執(zhí)行暫停
function outer() { let name = 1 function inner() { return name ++ } return inner } let g = outer() console.log(g()) // 2 console.log(g()) // 3 console.log(g()) // 4 console.log(g()) // 5
2、setTimeout函數(shù)傳遞參數(shù)
默認(rèn)的setTimeout是這樣的:
小編也曾經(jīng)這樣試驗(yàn)過(guò)
function f1(p) { console.log(p) } setTimeout(f1(666),3000) // 并沒(méi)有延時(shí),直接輸出666
要想通過(guò)延時(shí)來(lái)實(shí)現(xiàn)對(duì)函數(shù)傳遞參數(shù),這時(shí)候,閉包的作用就顯現(xiàn)出來(lái)了。
function f1(a) { function f2() { console.log(a); } return f2; } var fun = f1(1); setTimeout(fun,1000); // 一秒之后打印出1
3、回調(diào)
定義行為,然后把它關(guān)聯(lián)到某個(gè)用戶(hù)事件上(點(diǎn)擊或者按鍵)。代碼通常會(huì)作為一個(gè)回調(diào)(事件觸發(fā)時(shí)調(diào)用的函數(shù))綁定到事件。就像下面這塊代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>測(cè)試</title> </head> <body> <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" id="size-12">12</a> <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" id="size-20">20</a> <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" id="size-30">30</a> <script type="text/javascript"> function changeSize(size){ return function(){ document.body.style.fontSize = size + 'px'; }; } var size12 = changeSize(12); var size14 = changeSize(20); var size16 = changeSize(30); document.getElementById('size-12').onclick = size12; document.getElementById('size-20').onclick = size14; document.getElementById('size-30').onclick = size16; </script> </body> </html>
4、函數(shù)防抖
在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計(jì)時(shí)。
實(shí)現(xiàn)的關(guān)鍵就在于setTimeout
這個(gè)函數(shù),由于還需要一個(gè)變量來(lái)保存計(jì)時(shí),考慮維護(hù)全局純凈,可以借助閉包來(lái)實(shí)現(xiàn)。就像下面這樣:
/* * fn [function] 需要防抖的函數(shù) * delay [number] 毫秒,防抖期限值 */ function debounce(fn,delay){ let timer = null //借助閉包 return function() { if(timer){ clearTimeout(timer) //進(jìn)入該分支語(yǔ)句,說(shuō)明當(dāng)前正在一個(gè)計(jì)時(shí)過(guò)程中,并且又觸發(fā)了相同事件。所以要取消當(dāng)前的計(jì)時(shí),重新開(kāi)始計(jì)時(shí) timer = setTimeOut(fn,delay) }else{ timer = setTimeOut(fn,delay) // 進(jìn)入該分支說(shuō)明當(dāng)前并沒(méi)有在計(jì)時(shí),那么就開(kāi)始一個(gè)計(jì)時(shí) } } }
六、使用類(lèi)實(shí)現(xiàn)類(lèi)似閉包中隱藏內(nèi)部變量功能
上面是一個(gè)關(guān)于閉包的實(shí)際應(yīng)用,小編在晚上睡不著覺(jué)的時(shí)候,想起同樣的需求,是不是也可以通過(guò)類(lèi)來(lái)實(shí)現(xiàn)呢?最后經(jīng)過(guò)一頓折騰,答案是肯定的,就像這樣:
class Adder{ constructor(c){ this._c = c } increace(){ this._c ++ } decreace(){ this._c -- } get finalNum(){ return this._c } } let c = new Adder(1) c.increace() console.log(c.finalNum) // 2 c.increace() console.log(c.finalNum) // 3 c.increace() console.log(c.finalNum) // 4 c.decreace() console.log(c.finalNum) // 3
參考文章:
https://www.cnblogs.com/gg-qq...
相關(guān)文章
微信小程序 swiper制作tab切換實(shí)現(xiàn)附源碼
這篇文章主要介紹了微信小程序 swiper制作tab切換實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01Javascript使用integrity屬性進(jìn)行安全驗(yàn)證
這篇文章主要介紹了Javascript使用integrity屬性進(jìn)行安全驗(yàn)證,在html中,script標(biāo)簽可以通過(guò)src屬性引入一個(gè)js文件,引入的js文件可以是本地的,也可以是遠(yuǎn)程的,下面我們一起來(lái)看看文章詳細(xì)內(nèi)容2021-11-11JavaScript與JQuery框架基礎(chǔ)入門(mén)教程
這篇文章主要介紹了jQuery和JavaScript入門(mén)基礎(chǔ)知識(shí)學(xué)習(xí)指南,jQuery是當(dāng)下最主流人氣最高的JavaScript庫(kù),需要的朋友可以參考下2021-07-07JavaScript監(jiān)測(cè)數(shù)據(jù)類(lèi)型方法全面總結(jié)
這篇文章主要為大家介紹了JavaScript監(jiān)測(cè)數(shù)據(jù)類(lèi)型方法示例全面總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08微信小程序 用戶(hù)數(shù)據(jù)解密詳細(xì)介紹
這篇文章主要介紹了微信小程序 用戶(hù)數(shù)據(jù)解密詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-01-01Web?Components實(shí)現(xiàn)類(lèi)Element?UI中的Card卡片
這篇文章主要為大家介紹了Web?Components實(shí)現(xiàn)類(lèi)Element?UI中的Card卡片實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07微信小程序 時(shí)間格式化(util.formatTime(new Date))詳解
這篇文章主要介紹了微信小程序 時(shí)間格式化(util.formatTime(new Date))詳解的相關(guān)資料,這里附實(shí)例,一目了然很容易解決,需要的朋友可以參考下2016-11-11JavaScript知識(shí):構(gòu)造函數(shù)也是函數(shù)
構(gòu)造函數(shù)就是初始化一個(gè)實(shí)例對(duì)象,對(duì)象的prototype屬性是繼承一個(gè)實(shí)例對(duì)象。本文給大家分享javascript構(gòu)造函數(shù)詳解,對(duì)js構(gòu)造函數(shù)相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2021-08-08