站長(zhǎng)資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        簡(jiǎn)單了解JavaScript閉包

        本篇文章給大家?guī)?lái)了關(guān)于JavaScript的相關(guān)知識(shí),其中主要介紹了關(guān)于JavaScript閉包的相關(guān)問題,閉包的概念有很多版本,不同的地方對(duì)閉包的說(shuō)法不一,下面一起來(lái)看一下,希望對(duì)大家有幫助。

        簡(jiǎn)單了解JavaScript閉包

        什么是閉包?

        閉包的概念是有很多版本,不同的地方對(duì)閉包的說(shuō)法不一

        維基百科:在計(jì)算機(jī)科學(xué)中,閉包(英語(yǔ):Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是在支持頭等函數(shù)的編程語(yǔ)言中實(shí)現(xiàn)詞法綁定的一種技術(shù)。

        MDN: 閉包(closure)是一個(gè)函數(shù)以及其捆綁的周邊環(huán)境狀態(tài)(lexical environment詞法環(huán)境)的引用的組合。

        個(gè)人理解:

        • 閉包是一個(gè)函數(shù)(返回一個(gè)函數(shù))
        • 返回的函數(shù)保存了對(duì)外變量引用

        一個(gè)簡(jiǎn)單的示例

        function fn() {    let num = 1;    return function (n) {        return n + num     } }let rFn = fn()let newN = rFn(3) // 4
        登錄后復(fù)制

        num 變量作用域在 fn 函數(shù)中, rFn 函數(shù)卻能訪問 num 變量,這就是閉包函數(shù)能訪問外部函數(shù)變量。

        從瀏覽器調(diào)試和 VSCode Nodejs 調(diào)試看閉包

        • 瀏覽器

        簡(jiǎn)單了解JavaScript閉包

        • VS Code 配合 Node.js

        簡(jiǎn)單了解JavaScript閉包

        看到 Closure 中 fn 是閉包函數(shù),其中保存 num 變量。

        一個(gè)經(jīng)典的閉包:?jiǎn)尉€程事件機(jī)制+循環(huán)問題,以及解決辦法

        for (var i = 1; i <= 5; i++) {  setTimeout(() => {    console.log(i);   }, i * 1000); }
        登錄后復(fù)制

        登錄后復(fù)制

        輸出的結(jié)果都是 6,為什么?

        • for 循環(huán)是同步任務(wù)
        • setTimeout 異步任務(wù)

        for 循環(huán)一次,就會(huì)將 setTimeout 異步任務(wù)加入到瀏覽器的異步任務(wù)隊(duì)列中,同步任務(wù)完成之后,再?gòu)漠惒饺蝿?wù)中拿新任務(wù)在線程中執(zhí)行。由于 setTimeout 能夠訪問外部變量 i, 當(dāng)同步任務(wù)完成之后,i 已經(jīng)變成了6, setTimeout 中能夠訪問變量 i 都是 6。

        解決辦法1:使用 let 聲明

        for (var i = 1; i <= 5; i++) {  setTimeout(() => {    console.log(i);   }, i * 1000); }
        登錄后復(fù)制

        登錄后復(fù)制

        解決辦法2:自執(zhí)行函數(shù) + 閉包

        for (var i = 1; i <= 5; i++) {   (function(i){      setTimeout(() => {    console.log(i);   }, i * 1000)   })(i) }
        登錄后復(fù)制

        解決辦法3:setTimeout 傳遞第三參數(shù)

        第三個(gè)參數(shù)意思:附加參數(shù),一旦定時(shí)器到期,它們會(huì)作為參數(shù)傳遞給要執(zhí)行的函數(shù)

        for (var i = 1; i <= 5; i++) {  setTimeout((j) => {    console.log(j);   }, 1000 * i, i); }
        登錄后復(fù)制

        閉包與函數(shù)科里化

        function add(num) {  return function (y) {    return num + y;   }; };let incOneFn = add(1); let n = incOneFn(1);  // 2let decOneFn = add(-1); let m = decOneFn(1); // 0
        登錄后復(fù)制

        add 函數(shù)的參數(shù)保存了閉包函數(shù)變量。

        實(shí)際作用

        在函數(shù)式編程閉包有非常重要的作用,lodash 等早期工具函數(shù)彌補(bǔ) javascript 缺陷的工具函數(shù),有大量的閉包的使用場(chǎng)景。

        使用場(chǎng)景

        • 創(chuàng)建私有變量
        • 延長(zhǎng)變量生命周期

        節(jié)流函數(shù)

        防止?jié)L動(dòng)行為,過(guò)度執(zhí)行函數(shù),必須要節(jié)流, 節(jié)流函數(shù)接受 函數(shù) + 時(shí)間作為參數(shù),都是閉包中變量,以下是一個(gè)簡(jiǎn)單 setTimeout 版本:

        function throttle(fn, time=300){    var t = null;    return function(){        if(t) return;         t = setTimeout(() => {             fn.call(this);             t = null;         }, time);     } }
        登錄后復(fù)制

        防抖函數(shù)

        一個(gè)簡(jiǎn)單的基于 setTimeout 防抖的函數(shù)的實(shí)現(xiàn)

        function debounce(fn,wait){    var timer = null;    return function(){        if(timer !== null){            clearTimeout(timer);         }         timer = setTimeout(fn,wait);     } }
        登錄后復(fù)制

        React.useCallback 閉包陷阱問題

        問題說(shuō)明:父/子 組件關(guān)系, 父子組件都能使用 click 事件同時(shí)修改 state 數(shù)據(jù), 并且子組件拿到傳遞下的 props 事件屬性,是經(jīng)過(guò) useCallback 優(yōu)化過(guò)的。也就是這個(gè)被優(yōu)化過(guò)的函數(shù),存在閉包陷阱,(保存一直是初始 state 值)

        import { useState, useCallback, memo } from "react";const ChildWithMemo = memo((props: any) => {  return (    <div>       <button onClick={props.handleClick}>Child click</button>     </div>   ); });const Parent = () => {  const [count, setCount] = useState(1);  const handleClickWithUseCallback = useCallback(() => {    console.log(count);   }, []); // 注意這里是不能監(jiān)聽 count, 因?yàn)槊看巫兓紩?huì)重新綁定,造成造成子組件重新渲染    return (    <div>       <div>parent count : {count}</div>       <button onClick={() => setCount(count + 1)}>click</button>       <ChildWithMemo handleClick={handleClickWithUseCallback} />     </div>   ); };export default Parent
        登錄后復(fù)制

        • ChildWithMemo 使用 memo 進(jìn)行優(yōu)化,
        • handleClickWithUseCallback 使用 useCallback 優(yōu)化

        問題是點(diǎn)擊子組件時(shí)候,輸出的 count 是初始值(被閉包了)。

        解決辦法就是使用 useRef 保存操作變量函數(shù):

        import { useState, useCallback, memo, useRef } from "react";const ChildWithMemo = memo((props: any) => {  console.log("rendered children")  return (    <div>       <button onClick={() => props.countRef.current()}>Child click</button>     </div>   ); });const Parent = () => {  const [count, setCount] = useState(1);  const countRef = useRef<any>(null)    countRef.current = () => {    console.log(count);   }  return (    <div>       <div>parent count : {count}</div>       <button onClick={() => setCount(count + 1)}>click</button>       <ChildWithMemo countRef={countRef} />     </div>   ); };export default Parent
        登錄后復(fù)制

        針對(duì)這個(gè)問題,React 曾經(jīng)認(rèn)可過(guò)社區(qū)提出的增加 useEvent 方案,但是后面 useEvent 語(yǔ)義問題被廢棄了,對(duì)于渲染優(yōu)化 React 采用了編譯優(yōu)化的方案。其實(shí)類似的問題也會(huì)發(fā)生在 useEffect 中,使用時(shí)要注意閉包陷阱。

        性能問題

        • 閉包不要隨意定義,定義了一定找到合適的位置進(jìn)行銷毀。因?yàn)殚]包的變量保存在內(nèi)存中,不會(huì)被銷毀,占用較高的內(nèi)存。

        使用 chrome 面板功能 timeline + profiles 面板

        1. 打開開發(fā)者工具,選擇 Timeline 面板
        2. 在頂部的Capture字段里面勾選 Memory
        3. 點(diǎn)擊左上角的錄制按鈕。
        4. 在頁(yè)面上進(jìn)行各種操作,模擬用戶的使用情況。
        5. 一段時(shí)間后,點(diǎn)擊對(duì)話框的 stop 按鈕,面板上就會(huì)顯示這段時(shí)間的內(nèi)存占用情況。

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
        主站蜘蛛池模板: 一级香蕉精品视频在线播放| 国产这里有精品| 国产精品一区二区av不卡| 久久夜色精品国产噜噜噜亚洲AV | 国产最新进精品视频| 国产精品亚洲专区无码WEB| 99精品视频在线观看免费| 亚洲国产精品无码av| 亚洲国产精品一区二区第一页免 | 亚洲国产精品无码专区在线观看| 国产精品偷伦视频免费观看了| 99热亚洲色精品国产88| 亚洲av永久无码精品漫画| 欧美人与性动交α欧美精品成人色XXXX视频 | 国产AV午夜精品一区二区三区| 午夜精品一区二区三区免费视频| 欧美精品区一级片免费播放| 国产观看精品一区二区三区| 久久er国产精品免费观看2| 91精品国产乱码久久久久久| 日韩精品无码一区二区三区免费| 无码人妻精品一区二| 欧美精品播放| 欧美亚洲日本久久精品| 久久久久久久久久免免费精品| 国产精品毛片无码| 国产成人毛片亚洲精品| 囯产精品一区二区三区线| 国产AV国片精品一区二区| 91精品久久久久久无码| 四虎国产精品永久地址49| 久久精品国产只有精品2020| 国产成人亚洲精品| 精品国产福利第一区二区三区| 成人国产精品一区二区网站| 97视频在线观看这里只有精品 | 精品永久久福利一区二区| 久久精品欧美日韩精品| 国产乱子伦精品无码码专区| 精品久久久久久久无码| 成人国产精品免费视频|