<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          js--閉包與垃圾回收機(jī)制

          共 12275字,需瀏覽 25分鐘

           ·

          2021-03-03 10:59

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

            作者 |  丶Serendipity丶

          來源 |  urlify.cn/Jj2EJz

          76套java從入門到精通實(shí)戰(zhàn)課程分享

          前言

            閉包和垃圾回收機(jī)制常常作為前端學(xué)習(xí)開發(fā)中的難點(diǎn),也經(jīng)常在面試中遇到這樣的問題,本文記錄一下在學(xué)習(xí)工作中關(guān)于這方面的筆記。

          正文

           1.閉包

            閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。作為一個(gè)JavaScript開發(fā)者,理解閉包十分重要。

            1.1閉包是什么?

            閉包就是一個(gè)函數(shù)引用另一個(gè)函數(shù)的變量,內(nèi)部函數(shù)被返回到外部并保存時(shí)產(chǎn)生,(內(nèi)部函數(shù)的作用域鏈AO使用了外層函數(shù)的AO)

                 因?yàn)樽兞勘灰弥圆粫?huì)被回收,因此可以用來封裝一個(gè)私有變量,但是不必要的閉包只會(huì)增加內(nèi)存消耗。

            閉包是一種保護(hù)私有變量的機(jī)制,在函數(shù)執(zhí)行時(shí)形成私有的作用域,保護(hù)里面的私有變量不受外界干擾?;蛘哒f閉包就是子函數(shù)可以使用父函數(shù)的局部變量,還有父函數(shù)的參數(shù)。

            1.2閉包的特性

             ①函數(shù)嵌套函數(shù)

           ?、诤瘮?shù)內(nèi)部可以引用函數(shù)外部的參數(shù)和變量

           ?、蹍?shù)和變量不會(huì)被垃圾回收機(jī)制回收

            1.3理解閉包

            基于我們所熟悉的作用域鏈相關(guān)知識(shí),我們來看下關(guān)于計(jì)數(shù)器的問題,如何實(shí)現(xiàn)一個(gè)函數(shù),每次調(diào)用該函數(shù)時(shí)候計(jì)數(shù)器加一。

          var counter=0;
              function demo3(){
                  console.log(counter+=1);       
              }
              demo3();//1
              demo3();//2
              var counter=5;
              demo3();  //6

            上面的方法,如果在任何一個(gè)地方改變counter的值 計(jì)數(shù)器都會(huì)失效,javascript解決這種問題用到閉包,就是函數(shù)內(nèi)部?jī)?nèi)嵌函數(shù),再來看下利用閉包如何實(shí)現(xiàn)。

          function add() {
                      var counter = 0;
                      return function plus() {
                          counter += 1;
                          return counter
                      }      
                  }
                  var count=add()
                  console.log(count())//1
                  var counter=100
                  console.log(count())//2

            上面就是一個(gè)閉包使用的實(shí)例 ,函數(shù)add內(nèi)部?jī)?nèi)嵌一個(gè)plus函數(shù),count變量引用該返回的函數(shù),每次外部函數(shù)add執(zhí)行的時(shí)候都會(huì)開辟一塊內(nèi)存空間,外部函數(shù)的地址不同,都會(huì)重新創(chuàng)建一個(gè)新的地址,把plus函數(shù)嵌套在add函數(shù)內(nèi)部,這樣就產(chǎn)生了counter這個(gè)局部變量,內(nèi)次調(diào)用count函數(shù),該局部變量值加一,從而實(shí)現(xiàn)了真正的計(jì)數(shù)器問題。

            1.4閉包的主要實(shí)現(xiàn)形式

            這里主要通過兩種形式來學(xué)習(xí)閉包:

           ?、俸瘮?shù)作為返回值,也就是上面的例子中用到的。

          function showName(){
                          var name="xiaoming"
                          return function(){
                              return name
                          }
                      }
                      var name1=showName()
                      console.log(name1())

            閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

           ?、陂]包作為參數(shù)傳遞

          var num = 15
                      var foo = function(x){
                          if(x>num){
                              console.log(x)
                          }  
                      }
                      function foo2(fnc){
                          var num=30
                          fnc(25)
                      }
                      foo2(foo)//25

          上面這段代碼中,函數(shù)foo作為參數(shù)傳入到函數(shù)foo2中,在執(zhí)行foo2的時(shí)候,25作為參數(shù)傳入foo中,這時(shí)判斷的x>num的num取值是創(chuàng)建函數(shù)的作用域中的num,即全局的num,而不是foo2內(nèi)部的num,因此打印出了25。

            1.5閉包的優(yōu)缺點(diǎn)

            優(yōu)點(diǎn):

           ?、俦Wo(hù)函數(shù)內(nèi)的變量安全 ,實(shí)現(xiàn)封裝,防止變量流入其他環(huán)境發(fā)生命名沖突

           ?、谠趦?nèi)存中維持一個(gè)變量,可以做緩存(但使用多了同時(shí)也是一項(xiàng)缺點(diǎn),消耗內(nèi)存)

           ?、勰涿詧?zhí)行函數(shù)可以減少內(nèi)存消耗

            缺點(diǎn):

            ①其中一點(diǎn)上面已經(jīng)有體現(xiàn)了,就是被引用的私有變量不能被銷毀,增大了內(nèi)存消耗,造成內(nèi)存泄漏,解決方法是可以在使用完變量后手動(dòng)為它賦值為null;

           ?、谄浯斡捎陂]包涉及跨域訪問,所以會(huì)導(dǎo)致性能損失,我們可以通過把跨作用域變量存儲(chǔ)在局部變量中,然后直接訪問局部變量,來減輕對(duì)執(zhí)行速度的影響。

            1.6閉包的使用

          for (var i = 0; i < 5; i++) {
                setTimeout(function() {
                    console.log( i);
                }, 1000);
            }

            console.log(i);

            我們來看上面的問題,這是一道很常見的題,可這道題會(huì)輸出什么,一般人都知道輸出結(jié)果是 5,5,5,5,5,5,你仔細(xì)觀察可能會(huì)發(fā)現(xiàn)這道題還有很多巧妙之處,這6個(gè)5的輸出順序具體是怎樣的?5 -> 5,5,5,5,5 ,了解同步異步的人也不難理解這種情況,基于上面的問題,接下來思考如何實(shí)現(xiàn)5 -> 0,1,2,3,4這樣的順序輸出呢?

          for (var i = 0; i < 5; i++) {
                  (function(j) {  // j = i
                      setTimeout(function() {
                          console.log( j);
                      }, 1000);
                  })(i);
              }
              console.log( i);
          //5 -> 0,1,2,3,4

            這樣在for循環(huán)種加入匿名函數(shù),匿名函數(shù)入?yún)⑹敲看蔚膇的值,在同步函數(shù)輸出5的一秒之后,繼續(xù)輸出01234。

          for (var i = 0; i < 5; i++) {
                  setTimeout(function(j) {
                      console.log(j);
                  }, 1000, i);
              }
              console.log( i);
              //5 -> 0,1,2,3,4

            仔細(xì)查看setTimeout的api你會(huì)發(fā)現(xiàn)它還有第三個(gè)參數(shù),這樣就省去了通過匿名函數(shù)傳入i的問題。

          var output = function (i) {
                setTimeout(function() {
                    console.log(i);
                }, 1000);
            };

            for (var i = 0; i < 5; i++) {
                output(i);  // 這里傳過去的 i 值被復(fù)制了
            }

            console.log(i);
            //5 -> 0,1,2,3,4

            這里就是利用閉包將函數(shù)表達(dá)式作為參數(shù)傳遞到for循環(huán)中,同樣實(shí)現(xiàn)了上述效果。

          for (let i = 0; i < 5; i++) {
                setTimeout(function() {
                    console.log(new Date, i);
                }, 1000);
            }
            console.log(new Date, i);
            //5 -> 0,1,2,3,4

            知道let塊級(jí)作用域的人會(huì)想到上面的方法。但是如果要實(shí)現(xiàn)0 -> 1 -> 2 -> 3 -> 4 -> 5這樣的效果呢。

          for (var i = 0; i < 5; i++) {
                (function(j) {
                    setTimeout(function() {
                        console.log(new Date, j);
                    }, 1000 * j);  // 這里修改 0~4 的定時(shí)器時(shí)間
                })(i);
            }

            setTimeout(function() { // 這里增加定時(shí)器,超時(shí)設(shè)置為 5 秒
                console.log(new Date, i);
            }, 1000 * i);
            //0 -> 1 -> 2 -> 3 -> 4 -> 5

            還有下面的代碼,通過promise來實(shí)現(xiàn)。

          const tasks = [];
            for (var i = 0; i < 5; i++) {   // 這里 i 的聲明不能改成 let,如果要改該怎么做?
                ((j) => {
                    tasks.push(new Promise((resolve) => {
                        setTimeout(() => {
                            console.log(new Date, j);
                            resolve();  // 這里一定要 resolve,否則代碼不會(huì)按預(yù)期 work
                        }, 1000 * j);   // 定時(shí)器的超時(shí)時(shí)間逐步增加
                    }));
                })(i);
            }

            Promise.all(tasks).then(() => {
                setTimeout(() => {
                    console.log(new Date, i);
                }, 1000);   // 注意這里只需要把超時(shí)設(shè)置為 1 秒
            });
            //0 -> 1 -> 2 -> 3 -> 4 -> 5
          const tasks = []; // 這里存放異步操作的 Promise
            const output = (i) => new Promise((resolve) => {
                setTimeout(() => {
                    console.log(new Date, i);
                    resolve();
                }, 1000 * i);
            });

            // 生成全部的異步操作
            for (var i = 0; i < 5; i++) {
                tasks.push(output(i));
            }

            // 異步操作完成之后,輸出最后的 i
            Promise.all(tasks).then(() => {
                setTimeout(() => {
                    console.log(new Date, i);
                }, 1000);
            });
            //0 -> 1 -> 2 -> 3 -> 4 -> 5
          // 模擬其他語言中的 sleep,實(shí)際上可以是任何異步操作
          const sleep = (timeountMS) => new Promise((resolve) => {
              setTimeout(resolve, timeountMS);
          });

          (async () => {  // 聲明即執(zhí)行的 async 函數(shù)表達(dá)式
              for (var i = 0; i < 5; i++) {
                  if (i > 0) {
                      await sleep(1000);
                  }
                  console.log(new Date, i);
              }

              await sleep(1000);
              console.log(new Date, i);
          })();
          //0 -> 1 -> 2 -> 3 -> 4 -> 5

            上面的代碼中都用到了閉包,總之,閉包找到的是同一地址中父級(jí)函數(shù)中對(duì)應(yīng)變量最終的值。

            2.垃圾回收機(jī)制

            JavaScript 中的內(nèi)存管理是自動(dòng)執(zhí)行的,而且是不可見的。我們創(chuàng)建基本類型、對(duì)象、函數(shù)……所有這些都需要內(nèi)存。

            通常用采用的垃圾回收有兩種方法:標(biāo)記清除、引用計(jì)數(shù)。

            1、標(biāo)記清除

            垃圾收集器在運(yùn)行的時(shí)候會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記。然后,它會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的標(biāo)記。

            而在此之后再被加上標(biāo)記的變量將被視為準(zhǔn)備刪除的變量,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了。

            最后。垃圾收集器完成內(nèi)存清除工作,銷毀那些帶標(biāo)記的值,并回收他們所占用的內(nèi)存空間

            2.引用計(jì)數(shù)

            引用計(jì)數(shù)的含義是跟蹤記錄每個(gè)值被引用的次數(shù)。當(dāng)聲明了一個(gè)變量并將一個(gè)引用類型賦值給該變量時(shí),則這個(gè)值的引用次數(shù)就是1。

            相反,如果包含對(duì)這個(gè)值引用的變量又取得了另外一個(gè)值,則這個(gè)值的引用次數(shù)就減1。當(dāng)這個(gè)引用次數(shù)變成0時(shí),

            則說明沒有辦法再訪問這個(gè)值了,因而就可以將其所占的內(nèi)存空間給收回來。這樣,垃圾收集器下次再運(yùn)行時(shí),

            它就會(huì)釋放那些引用次數(shù)為0的值所占的內(nèi)存。

           

          總結(jié)

            以上就是本文的全部?jī)?nèi)容,希望給讀者帶來些許的幫助和進(jìn)步,方便的話點(diǎn)個(gè)關(guān)注,小白的成長(zhǎng)之路會(huì)持續(xù)更新一些工作中常見的問題和技術(shù)點(diǎn)。

           








          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ??????

          ??長(zhǎng)按上方微信二維碼 2 秒


          感謝點(diǎn)贊支持下哈 

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  自拍成人午夜视频 | 免费人成视频在线 | 玖玖视频这里只有精品 | 国产性交黄片 | 中文字幕在线成人 |