<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>

          深入淺出Javascript閉包

          共 3582字,需瀏覽 8分鐘

           ·

          2021-01-19 20:49

          來源 |?http://www.fly63.com/article/detial/10081

          一、引子

          閉包(closure)是 JavaScript 語言的一個(gè)難點(diǎn),面試時(shí)常被問及,也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。本文盡可能用簡單易懂的話,講清楚閉包的概念、形成條件及其常見的面試題。

          我們先來看一個(gè)例子:

          var n = 999;function f1() {console.log(n);}f1()?//?999

          上面代碼中,函數(shù)f1可以讀取全局變量n。但是,函數(shù)外部無法讀取函數(shù)內(nèi)部聲明的變量。

          function f1() {var n = 999;}console.log(n)// Uncaught ReferenceError: n is not defined
          上面代碼中,函數(shù)f1內(nèi)部聲明的變量n,函數(shù)外是無法讀取的。
          如果有時(shí)需要得到函數(shù)內(nèi)的局部變量。正常情況下,這是辦不到的,只有通過變通方法才能實(shí)現(xiàn)。那就是在函數(shù)的內(nèi)部,再定義一個(gè)函數(shù)。
          function f1() {var n = 999;function f2() {  console.log(n); // 999 }}

          上面代碼中,函數(shù)f2就在函數(shù)f1內(nèi)部,這時(shí)f1內(nèi)部的所有局部變量,對(duì)f2都是可見的。既然f2可以讀取f1的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎!

          二、閉包是什么

          我們可以對(duì)上面代碼進(jìn)行如下修改:

          function f1(){   var a = 999;   function f2(){    console.log(a);   }   return f2; // f1返回了f2的引用   }   var result = f1(); // result就是f2函數(shù)了   result();  // 執(zhí)行result,全局作用域下沒有a的定義,         //但是函數(shù)閉包,能夠把定義函數(shù)的時(shí)候的作用域一起記住,輸出999

          上面代碼中,函數(shù)f1的返回值就是函數(shù)f2,由于f2可以讀取f1的內(nèi)部變量,所以就可以在外部獲得f1的內(nèi)部變量了。

          閉包就是函數(shù)f2,即能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。由于在JavaScript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取內(nèi)部變量,因此可以把閉包簡單理解成“定義在一個(gè)函數(shù)內(nèi)部的函數(shù)”。

          閉包最大的特點(diǎn),就是它可以“記住”誕生的環(huán)境,比如f2記住了它誕生的環(huán)境f1,所以從f2可以得到f1的內(nèi)部變量。在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

          那到底什么是閉包呢?

          當(dāng)函數(shù)可以記住并訪問所在的詞法作用域,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行,這就產(chǎn)生了閉包。?----《你不知道的Javascript上卷》

          我個(gè)人理解,閉包就是函數(shù)中的函數(shù)(其他語言不能函數(shù)再套函數(shù)),里面的函數(shù)可以訪問外面函數(shù)的變量,外面的變量的是這個(gè)內(nèi)部函數(shù)的一部分。

          閉包形成的條件

          • 函數(shù)嵌套

          • 內(nèi)部函數(shù)引用外部函數(shù)的局部變量

          三、閉包的特性

          每個(gè)函數(shù)都是閉包,每個(gè)函數(shù)天生都能夠記憶自己定義時(shí)所處的作用域環(huán)境。把一個(gè)函數(shù)從它定義的那個(gè)作用域,挪走,運(yùn)行。這個(gè)函數(shù)居然能夠記憶住定義時(shí)的那個(gè)作用域。不管函數(shù)走到哪里,定義時(shí)的作用域就帶到了哪里。接下來我們用兩個(gè)例子來說明這個(gè)問題:

          //例題1var inner;function outer(){var a=250;inner=function(){alert(a);//這個(gè)函數(shù)雖然在外面執(zhí)行,但能夠記憶住定義時(shí)的那個(gè)作用域,a是250  }}outer();var a=300;inner();//一個(gè)函數(shù)在執(zhí)行的時(shí)候,找閉包里面的變量,不會(huì)理會(huì)當(dāng)前作用域。
          //例題2function outer(x){  function inner(y){  console.log(x+y);  }return inner;}var inn=outer(3);//數(shù)字3傳入outer函數(shù)后,inner函數(shù)中x便會(huì)記住這個(gè)值inn(5);//當(dāng)inner函數(shù)再傳入5的時(shí)候,只會(huì)對(duì)y賦值,所以最后彈出8

          四、閉包的內(nèi)存泄漏

          棧內(nèi)存提供一個(gè)執(zhí)行環(huán)境,即作用域,包括全局作用域和私有作用域,那他們什么時(shí)候釋放內(nèi)存的?

          • 全局作用域----只有當(dāng)頁面關(guān)閉的時(shí)候全局作用域才會(huì)銷毀

          • 私有的作用域----只有函數(shù)執(zhí)行才會(huì)產(chǎn)生

          一般情況下,函數(shù)執(zhí)行會(huì)形成一個(gè)新的私有的作用域,當(dāng)私有作用域中的代碼執(zhí)行完成后,我們當(dāng)前作用域都會(huì)主動(dòng)的進(jìn)行釋放和銷毀。但當(dāng)遇到函數(shù)執(zhí)行返回了一個(gè)引用數(shù)據(jù)類型的值,并且在函數(shù)的外面被一個(gè)其他的東西給接收了,這種情況下一般形成的私有作用域都不會(huì)銷毀。

          如下面這種情況:

          function fn(){var num=100;return function(){  }}var?f=fn();//fn執(zhí)行形成的這個(gè)私有的作用域就不能再銷毀了

          也就是像上面這段代碼,fn函數(shù)內(nèi)部的私有作用域會(huì)被一直占用的,發(fā)生了內(nèi)存泄漏。所謂內(nèi)存泄漏指任何對(duì)象在您不再擁有或需要它之后仍然存在。閉包不能濫用,否則會(huì)導(dǎo)致內(nèi)存泄露,影響網(wǎng)頁的性能。閉包使用完了后,要立即釋放資源,將引用變量指向null。

          接下來我們看下有關(guān)于內(nèi)存泄漏的一道經(jīng)典面試題:

            function outer(){  var num=0;//內(nèi)部變量  return function add(){//通過return返回add函數(shù),就可以在outer函數(shù)外訪問了  num++;//內(nèi)部函數(shù)有引用,作為add函數(shù)的一部分了  console.log(num);  }; }  var func1=outer();  func1();//實(shí)際上是調(diào)用add函數(shù), 輸出1  func1();//輸出2 因?yàn)閛uter函數(shù)內(nèi)部的私有作用域會(huì)一直被占用  var func2=outer();  func2();// 輸出1  每次重新引用函數(shù)的時(shí)候,閉包是全新的。  func2();// 輸出2

          五、閉包的作用

          1.可以讀取函數(shù)內(nèi)部的變量。

          2.可以使變量的值長期保存在內(nèi)存中,生命周期比較長。因此不能濫用閉包,否則會(huì)造成網(wǎng)頁的性能問題

          3.可以用來實(shí)現(xiàn)js模塊。

          js模塊:具有特定功能的js文件,將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的),只向外暴露一個(gè)包信n個(gè)方法的對(duì)象或函數(shù),模塊的使用者,只需要通過模塊暴露的對(duì)象調(diào)用方法來實(shí)現(xiàn)對(duì)應(yīng)的功能。

          具體請(qǐng)看下面的例子:

          //index.html文件
          //myModule.js文件(function () {  var msg = 'Beijing'//私有數(shù)據(jù)  //操作數(shù)據(jù)的函數(shù)  function doSomething() {    console.log('doSomething() '+msg.toUpperCase())  }  function doOtherthing () {    console.log('doOtherthing() '+msg.toLowerCase())  }  //向外暴露對(duì)象(給外部使用的兩個(gè)方法)  window.myModule2 = {    doSomething: doSomething,    doOtherthing: doOtherthing  }})()

          六、閉包的運(yùn)用

          我們要實(shí)現(xiàn)這樣的一個(gè)需求: 點(diǎn)擊某個(gè)按鈕, 提示"點(diǎn)擊的是第n個(gè)按鈕",此處我們先不用事件代理:

          .....

          萬萬沒想到,點(diǎn)擊任意一個(gè)按鈕,后臺(tái)都是彈出“第四個(gè)”,這是因?yàn)閕是全局變量,執(zhí)行到點(diǎn)擊事件時(shí),此時(shí)i的值為3。那該如何修改,最簡單的是用let聲明i

           for (let i = 0; i < btns.length; i++) {      btns[i].onclick = function () {        console.log('第' + (i + 1) + '個(gè)')      }????}

          另外我們可以通過閉包的方式來修改:

            for (var i = 0; i < btns.length; i++) {      (function (j) {        btns[j].onclick = function () {          console.log('第' + (j + 1) + '個(gè)')        }      })(i)    }

          本文完?


          瀏覽 66
          點(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>
                  黄色片一级 | 看日韩的黄色片 | 成人午夜A片免费看 | 日本无码少妇内谢视频 | 国产一级二级三级片 |