js不得不聊--關(guān)于閉包與作用域

最近看了js高級(jí)程序,書(shū)上對(duì)于閉包的解釋是:''閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量''.我覺(jué)得過(guò)于抽象,經(jīng)過(guò)一番查閱折騰,現(xiàn)在來(lái)談?wù)勎易约旱睦斫?希望對(duì)大家有所幫助.

  • 談閉包之前,還是先談?wù)勛饔糜?/strong>
    ①作用域:
    在之前的文章中就已經(jīng)提到過(guò),作用域簡(jiǎn)單來(lái)說(shuō)就是某個(gè)變量有(起)作用的范圍;它的規(guī)則是內(nèi)層的作用域可以訪問(wèn)外層的作用域,但是反過(guò)來(lái)不行
     function foo(){
        var b = "10";
    }
     console.log(b);//報(bào)錯(cuò),b is not defined

在上面的demo中,全局是訪問(wèn)不到變量b的,外層無(wú)法訪問(wèn)內(nèi)部變量.
②作用域鏈搜索原則:
在作用域中如果訪問(wèn)(讀取|設(shè)置)某個(gè)變量,先在當(dāng)前作用域中搜索,如果找到那么就直接使用;
如果沒(méi)有找到,那么就向上一級(jí)作用域中繼續(xù)搜索,找到則使用,沒(méi)有找到就重復(fù)上面的過(guò)程,直到0級(jí)作用域鏈;

    var num = 10;
    function f1(){
        console.log(num);       //undefined 函數(shù)內(nèi)部變量提升,結(jié)果并不是10
        num = 66;
        console.log(num);               // 66
        function f2(){
            var num = 99;
            console.log(num);           // 99
        }
        var num = 1000;
        f2();
        console.log(num);            //1000
    }
    f1();
    console.log(num);               //10 只能訪問(wèn)全局變量

了解完作用域的概念之后,對(duì)書(shū)上閉包的解釋相信大家也有了一定的理解了,既然外面作用域不能訪問(wèn)內(nèi)部作用域的變量,那如果我們需要訪問(wèn)的話,該怎么解決這個(gè)問(wèn)題呢?于是,閉包就誕生了.

  • 我對(duì)于閉包的理解
    我對(duì)于閉包的理解,就是一種能提供一種間接訪問(wèn)封閉空間中私有數(shù)據(jù)的方法,你也可以理解為外部作用域訪問(wèn)內(nèi)部作用域變量的方法.
    那么這個(gè)方法是如何實(shí)現(xiàn)的呢?
    閉包二字單純來(lái)講,所謂閉,就是指封閉,包,就是指包裹、包裝起來(lái)。JavaScript中沒(méi)有塊級(jí)作用域,函數(shù)是唯一一個(gè)可以創(chuàng)建作用域的對(duì)象,那么想封閉一個(gè)作用域,必須使用函數(shù)來(lái)包裹閉合。
function f1(){
     var num = 10;
     return function(){
         console.log(num);
     };
}
var f2=f1();
f2();// 10

上面的案例就是一個(gè)典型的閉包,外面作用域訪問(wèn)了函數(shù)內(nèi)部的變量num,那么這是怎么做到的呢?
代碼中我們?cè)趂1函數(shù)中return了一個(gè)函數(shù)f2,我們調(diào)用f1函數(shù)的結(jié)果為f2函數(shù),在f2函數(shù)調(diào)用時(shí),會(huì)先創(chuàng)建一個(gè)執(zhí)行環(huán)境,以及相應(yīng)的作用域鏈,在函數(shù)執(zhí)行中,為讀取和寫(xiě)入變量的值,就需要在作用域鏈中查找變量,對(duì)于f2函數(shù),f1是它的外部作用域,所以自然能夠訪問(wèn)f1中的變量num,不知不覺(jué)地,全局作用域就訪問(wèn)了f1中變量num的值。

  • 閉包和for循環(huán)
    《你不知道的JavaScript》中有這樣一個(gè)案例:
for (var i = 1; i <= 5; i++) {
    setTimeout( function timer(){
        console.log(i);
    },i*1000);
}

代碼的運(yùn)行結(jié)果:每秒輸出一次,且輸出了五次6.
setTimeout是傳入了一個(gè)函數(shù),延遲一段時(shí)間把這個(gè)函數(shù)添加到隊(duì)列當(dāng)中,并不是延遲一段時(shí)間之后就執(zhí)行函數(shù),這個(gè)函數(shù)要在所有其他函數(shù)執(zhí)行完畢之后才開(kāi)始執(zhí)行,在這之前,for循環(huán)早已經(jīng)執(zhí)行完畢,所以每次打印的值都是for循環(huán)的最終i值6,且每秒輸出一次。
在這里,定時(shí)器里的函數(shù)無(wú)法獲取其作用域范圍的值(i的值),根本原因是定時(shí)器是一個(gè)異步事件,要解決這個(gè)問(wèn)題,我們?cè)诤瘮?shù)上加上了一個(gè)立即執(zhí)行函數(shù)()();使得每一次循環(huán)的時(shí)候定時(shí)器函數(shù)立即執(zhí)行,獲取i的打印值。

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

當(dāng)然,Es6中使用let申明變量,申明了塊級(jí)作用域也起到了同樣的作用,這里我沒(méi)有深入了解,暫不解析。

for (let i = 1; i <= 5; i++) {
    setTimeout( function timer(){
        console.log(i);//1,2,3,4,5
    },i*1000);
}

好了,就寫(xiě)這么些,希望大家看了能夠有所收獲,不足之處也歡迎指正,O(∩_∩)O謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ● 閉包基礎(chǔ) ● 閉包作用 ● 閉包經(jīng)典例子 ● 閉包應(yīng)用 ● 閉包缺點(diǎn) ● 參考資料 1、閉包基礎(chǔ) 作用域和作...
    lzyuan閱讀 1,036評(píng)論 0 0
  • JS 函數(shù) 函數(shù)分為兩類具名函數(shù)、匿名函數(shù),其變型可以包括自執(zhí)行函數(shù)、遞歸函數(shù) 具名函數(shù)含有名字的函數(shù)functi...
    月光在心中閱讀 1,035評(píng)論 0 10
  • 一、執(zhí)行環(huán)境 所有變量(包括基本類型和引用類型)都存在一個(gè)執(zhí)行環(huán)境(作用域)當(dāng)中,這個(gè)執(zhí)行環(huán)境決定了變量的生命周期...
    張延偉閱讀 532評(píng)論 1 1
  • 閉包(closure)是Javascript語(yǔ)言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。 一、變量...
    zock閱讀 1,118評(píng)論 2 6
  • 這場(chǎng)會(huì)戰(zhàn)最終以中國(guó)失敗而告終,但我相信,這個(gè)結(jié)果是當(dāng)時(shí)那個(gè)年代的政客和將軍們,事先早以預(yù)料的,一點(diǎn)也不會(huì)感到意外,...
    梁宵閱讀 1,807評(píng)論 2 6

友情鏈接更多精彩內(nèi)容