日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

js 闭包及其相关知识点理解

發(fā)布時間:2025/4/9 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js 闭包及其相关知识点理解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文結(jié)合個人學(xué)習(xí)及實踐,對閉包及相關(guān)知識點進行總結(jié)記錄,歡迎讀者提出任何不足之處

一、js變量

二、作用域(scope)

三、[[scope]] 和 scope chain

四、作用域(scope)和關(guān)鍵字(this)

五、閉包實例理解 及 垃圾回收

?

一、js變量

  在 ECMAScript 中,變量可以存在兩種類型的值,即原始值和引用值。

  原始值:存儲在棧區(qū)(stack)的簡單數(shù)據(jù)段,變量所在的位置直接存儲變量的值。

      原始值包括:Undefined、Null、Boolean、Number 和 String 型

  引用值:存儲在堆區(qū)(heap)中的對象,變量所在的位置存的是地址指針(pointer),指向存儲對象在內(nèi)存中的地址

  

?

二、作用域(scope)

  1.作用域

    作用域指代碼當前的上下文環(huán)境,即代碼可以訪問和可以被訪問的區(qū)域。

?

  2.全局作用域(一個頁面一般只有一個全局作用域)

    <script>  

    //這里是全局作用域

    var myname1="yaoming";

    </script>

?

  3.本地作用域/局部作用域

    <script>  

    //這里是全局作用域

    var myname1="yaoming";

    var myfun=function(){

      console.log(myname1);  //yaoming

      var myname2="liuxiang";

      //這里是本地作用域

    }

    console.log(myname2);

    //myname2 is not defined

    </script>

    myname1打印出yaoming,是因為局部域可以訪問全局域中的變量(反之則不行)

    myname2未定義,是因為你全局域不能訪問局部域內(nèi)的變量

?

  4.函數(shù)域

    所有域都只能由函數(shù)域所創(chuàng)建

    <script>  

    // 全局作用域

    var?myFunction?=?function?()?{ ??

      // 局部作用域1?

      var?myOtherFunction?=?function?()?{ ????

                //?局部作用域2?

                };

     };

    注:for循環(huán)、while等循環(huán)都不能創(chuàng)建局部作用域

    //這里是全局作用域

    for(var i=0;i<10;i++){        

      //code

    }

    console.log(i);  //打印出10

    </script>  

    i 值為10,i 依然屬于全局域,是全局變量,所以循環(huán)不能創(chuàng)建局部作用域

?

  5.作用域鏈(scope chain,Scope Chain是一個鏈表,js中的閉包就是通過作用域鏈實現(xiàn)的

    <script>  

    // 全局作用域

    var?myFunction1 =?function?()?{ ??

      // 局部作用域1 (scope1)

      var?myOtherFunction1 =?function?()?{ ????

                //?局部作用域2(scope2?

                };

     };

    var?myFunction2 =?function?()?{ ??

?

      // 局部作用域3? (scope3)

?

      var?myOtherFunction2 =?function?()?{ ????

?

                //?局部作用域4? (scope4)

?

                };

?

     };

    </script>  

    

    圖解:    

    上圖棧區(qū)中的var a 和 var myFun1 在全局區(qū)域

    fun對應(yīng)myFunction1,在scope1中

    fun2對應(yīng)myOtherFunction1,在scope2中

    

    myOtherFunction1中形成的作用域鏈:scope1--》scope2--》全局作用域

    說明:a.scope1scope1scope1 中可以訪問scope2和全局作用域中的任何變量

       b.如果 scope2中使用了 i 變量但是沒有定義 i 變量,那么它會往其上級作用域?qū)ふ?i 變量直到找到為止,找不到為null。

       上圖:fun2 中使用了變量 i?

       第一步:在scope2 中尋找 i 變量,找到則停止,否則進入下一步

       第二步:在上一級域 scope1中繼續(xù)尋找 i 變量,同理找到停止,否則下一步

       第三步:在scope1的上級作用域 全局作用域中 尋找 i 變量 ,找到停止,否則變量未定義        

    

    myOtherFunction2中形成的作用域鏈:scope4--》scope3--》全局作用域 (此處同上)

?

?

  6.閉包(closure)/詞法作用域/靜態(tài)作用域

    當B函數(shù)嵌套在A函數(shù)內(nèi),B中引用了A作用域中的變量,

    并且B在A的外部調(diào)用了B函數(shù),B函數(shù)是閉包函數(shù)。

    <script>  

    //scope global

    function a(){

      var myname="liuxiang";

      return function b(){

        console.log(myname);

        //此作用域訪問上級作用域的 myname變量

      }

    }

    var c=a();  //c就是函數(shù)b ,在b的外層函數(shù)a之外被使用,那么此時就形成了閉包

    c();     ? //此處打印出 liuxiang

    console.log(this);

    </script>  

?

    閉包函數(shù)會擁有許多變量和綁定了這些變量的環(huán)境的表達式   

    console.log(this);打印出window對象,從全局對象window中找到 變量c并展開如下:

    

    由上圖可以看到,全局變量 c 指向了內(nèi)部函數(shù) b

    函數(shù)b的作用域scope=A0+[[scopes]],其中[[scopes]]為函數(shù)b的屬性,此屬性包含了一個與其形成閉包的函數(shù)a的Closure(a)作用域,和一個Global全局作用域

    Closure(a)中包含所有 b函數(shù)中使用到的變量

    Gloal 包含所有的全局變量

?

三、? VO/AO, scope chain , [[scope]]?和 scope

  JS 代碼的執(zhí)行

    在js代碼執(zhí)行之前,js引擎會在全局域創(chuàng)建一個 VO (變量對象) ,在每一個函數(shù)中的局部域創(chuàng)建一個 AO (活動對象)

    VO 指向?qū)儆谌钟虻乃袑ο?#xff0c;進入js代碼塊的時候即被創(chuàng)建

    AO 活動對象是進入函數(shù)上下文時被創(chuàng)建的,指向局部作用域的所有對象

  作用域鏈

    作用域鏈正是內(nèi)部上下文 所有變量對象 和 所有父變量對象 的列表。

    仍然以? 二、作用域 中的第 5 點 中的代碼為例

    myOtherFunction1 上下文的作用域鏈 為 AO(myOtherFunction1) AO(myFunction1) 和 VO(global)    

?

  [[scope]]

    [[scope]]屬性是在當前函數(shù)被定義時確定,[[scope]]屬性是所有父變量的層級鏈,位于當前的 AO 上下文 之上。

    函數(shù)之所有能訪問 上級作用域中的對象,就是[[scope]]屬性來實現(xiàn)的。

?

  scope的定義:    

    scope=AO+[[scope]];? 當前的 AO 是作用域 數(shù)組的第一個對象,即從當前活動對象 往上級查找 ,則局部變量 比 父級變量 有更高的優(yōu)先級。

    以 二、作用域 中的第 5 點 中的代碼為例 

    myOtherFunction1Context.Scope=? myOtherFunction1Context.AO + myOtherFunction1.[[Scope]]

                    =? myOtherFunction1Context.AO +? myFunction1Context.AO + myFunction1.[[Scope]]

                    =? myOtherFunction1Context.AO +? myFunction1Context.AO + globalContext.VO

    myOtherFunction1的scope數(shù)組是 myOtherFunction1Context.Scope= [ myOtherFunction1Context.AO, myFunction1Context.AO, globalContext.VO ];

?

四、作用域(scope)和關(guān)鍵字(this)

?

五、閉包實例理解 及 垃圾回收

  現(xiàn)有數(shù)組b,b中包含三個人的姓名和年齡信息。現(xiàn)在通過調(diào)用sayHello 方法,分別為每一個對象添加一個說出自己名字的方法。

  代碼實現(xiàn)如下:

<script>
var b=[
?? ?{"name":"yaoming",age:40},
?? ?{"name":"liuxiang",age:38},
?? ?{"name":"lining",age:50}
????? ];

function addSayHello(){
?? ?for(var j=0;j<b.length;j++){
?? ??? ?b[j].sayHello=function(){
?? ??? ??? ?return "hello,i am "+b[j].name;
?? ??? ?};
?? ?}
?? ?--j;
}


addSayHello();
console.log(b[0].name+"說:"+b[0].sayHello());
console.log(b[1].name+"說:"+b[1].sayHello());
console.log(b[2].name+"說:"+b[2].sayHello());
?? ?
</script>

控制臺輸入如下:

  

  發(fā)現(xiàn) 每個人的sayHello 都會打印出 我是 lining,這并不是我們想要的結(jié)果。

  分析:

    addSayHello 執(zhí)行完畢之后,全局作用域 里 b數(shù)組中的每個成員都有了自己的sayHello方法,

    該方法的表達式:function(){return "hello,i am "+b[j].name;};

    當調(diào)用b[0].sayHello()時,sayHello.AO中無 j 變量的定義,那么下一步,會到 addSayHello.AO 中尋找 j 變量,此時找到了? j 變量,此時 j 變量的值為2

    因此 b[0].sayHello()會返回?"hello,i am "+b[2].name;.同理所有人調(diào)用sayHello方法都會返回"hello,i am "+b[2].name;

?

  正確的方法:

<script>
var b=[
?? ?{"name":"yaoming",age:40},
?? ?{"name":"liuxiang",age:38},
?? ?{"name":"lining",age:50}
????? ];

function addSayHello(){
?? ?for(var j=0;j<b.length;j++){
?? ??? ?b[j].sayHello=(function(index){
?? ??? ??? ?return function(){
?? ??? ??? ??? ?return "hello,i am "+b[index].name;
?? ??? ??? ?}
?? ??? ?})(j);
?? ?}
?? ?--j;
}


addSayHello();

console.log(b[0].name+"說:"+b[0].sayHello());
console.log(b[1].name+"說:"+b[1].sayHello());
console.log(b[2].name+"說:"+b[2].sayHello());
?? ?
</script>

正確的控制臺運行結(jié)果:

思路:錯誤的方法中,sayHello 方法中找不到變量j,上級addSayHello.AO 中只保存其作用域中的變量 j(保存為循環(huán)執(zhí)行后j的最終值),造成所有方法訪問同一變量。

   如果我們能讓每個 sayHello 方法在 sayHello.AO 中保存自己所需的變量,就解決了剛剛的問題。在上面正確的方法中,每次循環(huán)都把當前 j 對應(yīng)的變量以參數(shù)的形式傳給內(nèi)層函數(shù),在內(nèi)層函數(shù)的作用域中保存當前的值即可。

  

  總結(jié):

  在錯誤的方法中,內(nèi)層sayhello() 函數(shù)和 外層函數(shù) addSayHello 形成了閉包,內(nèi)層變量 j 永遠都指向 外層函數(shù) addSayHello 中的 j變量。

  如下圖,每一個對象的函數(shù)作用域中 都指向 j=2

  

  改進的方法中,sayhello() 函數(shù) 為一個自執(zhí)行函數(shù)返回的一個匿名函數(shù)。 sayHello對應(yīng)的匿名函數(shù) 和 其外層的自執(zhí)行函數(shù) 形成閉包,這里 sayHello對應(yīng)的匿名函數(shù)中的   index變量 會指向 其自身的 外層自執(zhí)行函數(shù),其中每個自執(zhí)行函數(shù)的AO里都分別保存了運行時 j 變量的副本 index。

  如下圖,每一個對象的函數(shù)作用域中 都指向 其自身對應(yīng)的下標:index:index

  

?

  JS的垃圾回收機制:

  找到那些不被使用的變量,然后釋放其所占用的內(nèi)存

?

  ?問:為什么閉包中用到的變量會保存在內(nèi)存中?

  因為 全局變量 保存了對內(nèi)部函數(shù)的引用。所以, 內(nèi)部函數(shù),及其所綁定的上下文環(huán)境均被使用,因此變量不會被釋放,而是保存在內(nèi)存中。

?

轉(zhuǎn)載于:https://www.cnblogs.com/ahguSH/p/6087666.html

總結(jié)

以上是生活随笔為你收集整理的js 闭包及其相关知识点理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。