技術(shù)頻道導(dǎo)航
HTML/CSS
.NET技術(shù)
IIS技術(shù)
PHP技術(shù)
Js/JQuery
Photoshop
Fireworks
服務(wù)器技術(shù)
操作系統(tǒng)
網(wǎng)站運(yùn)營

贊助商

分類目錄

贊助商

最新文章

搜索

一個(gè)示例,就能讓你了解JS閉包的功能作用是什么

作者:admin    時(shí)間:2022-6-3 7:24:58    瀏覽:

在一個(gè)函數(shù)外面加一個(gè)括號(hào),就成為了一個(gè)閉包,說實(shí)在的,閉包函數(shù)在實(shí)際編程中用得并不多,但是這卻是學(xué)習(xí)JavaScript必須掌握的知識(shí),并且,閉包是前端考試/面試的必考題。因此,我們要學(xué)會(huì)和懂得使用這個(gè)知識(shí)點(diǎn)。在本文中,將通過一個(gè)示例,讓你了解閉包的功能作用是什么。

一個(gè)示例,讓你了解JS閉包的功能作用是什么

示例

場景設(shè)計(jì):有個(gè)函數(shù)體是一個(gè)計(jì)數(shù)器,我現(xiàn)在要讓計(jì)數(shù)器初始值增加3,如何編寫代碼?

普通函數(shù)實(shí)現(xiàn)的思路

使用普通函數(shù)實(shí)現(xiàn)的思路是這樣:

var counter = 0;
function add() {
   return counter += 1;
}
add();
add();
add();// 計(jì)數(shù)器現(xiàn)在為 3

輸出

現(xiàn)在我們已經(jīng)達(dá)到了目的,可是問題來了,代碼中的任何一個(gè)函數(shù)都可以隨意改變counter的值,因?yàn)?code>counter是一個(gè)全局變量,所以這個(gè)計(jì)數(shù)器并不完美。那我們把counter放在add函數(shù)里面不就好了么?

function add() {
    var counter = 0;
    return counter += 1;

add();
add();
add();// 本意是想輸出 3, 但輸出的都是 1 

輸出

所以這樣做的話,每次調(diào)用add函數(shù),counter的值都要被初始化為0,還是達(dá)不到我們的目的。

使用閉包的實(shí)現(xiàn)思路

這時(shí)候我們可以用閉包去解決這個(gè)問題了,先看代碼。

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
add();
add();
add();// 計(jì)數(shù)器為 3

這時(shí)候我們完美實(shí)現(xiàn)了計(jì)數(shù)器。這段非常精簡,可以拆分成如下等價(jià)代碼。

function outerFunction () {
     var counter = 0;
     function innerFunction (){
         return counter += 1;
     }
     return innerFunction;
}
var add = outerFunction();
add();
add();
add();// 計(jì)數(shù)器為 3

輸出

這時(shí)候的add就形成了一個(gè)閉包。一個(gè)閉包由兩部分組成,函數(shù)和創(chuàng)建該函數(shù)的環(huán)境。環(huán)境是由環(huán)境中的局部變量組成的。對于閉包add來說,它由函數(shù)innerFunction和變量counter組成,所以這時(shí)候add是可以訪問變量counter的。

結(jié)論

所以閉包的功能就是使一個(gè)函數(shù)能訪問另一個(gè)函數(shù)作用域中的變量。形成閉包之后,該變量不會(huì)被垃圾回收機(jī)制回收。

閉包的原理其實(shí)還是作用域。

知識(shí)擴(kuò)展

什么是閉包

一個(gè)函數(shù)和對其周圍狀態(tài)(lexical environment,詞法環(huán)境)的引用捆綁在一起(或者說函數(shù)被引用包圍),這樣的組合就是閉包(closure)。也就是說,閉包讓你可以在一個(gè)內(nèi)層函數(shù)中訪問到其外層函數(shù)的作用域。在 JavaScript 中,每當(dāng)創(chuàng)建一個(gè)函數(shù),閉包就會(huì)在函數(shù)創(chuàng)建的同時(shí)被創(chuàng)建出來。

詞法作用域

請看下面的代碼:

function init() {
  var name = "WebKaka"; // name 是一個(gè)被 init 創(chuàng)建的局部變量
  function displayName() { // displayName() 是內(nèi)部函數(shù),一個(gè)閉包
      console.log(name); // 使用了父函數(shù)中聲明的變量
  }
  displayName();
}
init();

輸出

init() 創(chuàng)建了一個(gè)局部變量 name 和一個(gè)名為 displayName() 的函數(shù)。displayName() 是定義在 init() 里的內(nèi)部函數(shù),并且僅在 init() 函數(shù)體內(nèi)可用。請注意,displayName() 沒有自己的局部變量。然而,因?yàn)樗梢栽L問到外部函數(shù)的變量,所以 displayName() 可以使用父函數(shù) init() 中聲明的變量 name 。

運(yùn)行該代碼后發(fā)現(xiàn), displayName() 函數(shù)內(nèi)的 log() 語句成功顯示出了變量 name 的值(該變量在其父函數(shù)中聲明)。這個(gè)詞法作用域的例子描述了分析器如何在函數(shù)嵌套的情況下解析變量名。詞法(lexical)一詞指的是,詞法作用域根據(jù)源代碼中聲明變量的位置來確定該變量在何處可用。嵌套函數(shù)可訪問聲明于它們外部作用域的變量。

示例

現(xiàn)在來看以下例子 :

function makeFunc() {
    var name = "WebKaka";
    function displayName() {
        console.log(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();

輸出

 

運(yùn)行這段代碼的效果和之前 init() 函數(shù)的示例完全一樣。其中不同的地方(也是有意思的地方)在于內(nèi)部函數(shù) displayName() 在執(zhí)行前,從外部函數(shù)返回。

第一眼看上去,也許不能直觀地看出這段代碼能夠正常運(yùn)行。在一些編程語言中,一個(gè)函數(shù)中的局部變量僅存在于此函數(shù)的執(zhí)行期間。一旦 makeFunc() 執(zhí)行完畢,你可能會(huì)認(rèn)為 name 變量將不能再被訪問。然而,因?yàn)榇a仍按預(yù)期運(yùn)行,所以在 JavaScript 中情況顯然與此不同。

原因在于,JavaScript 中的函數(shù)會(huì)形成了閉包。 閉包是由函數(shù)以及聲明該函數(shù)的詞法環(huán)境組合而成的。該環(huán)境包含了這個(gè)閉包創(chuàng)建時(shí)作用域內(nèi)的任何局部變量。在本例子中,myFunc 是執(zhí)行 makeFunc 時(shí)創(chuàng)建的 displayName 函數(shù)實(shí)例的引用。displayName 的實(shí)例維持了一個(gè)對它的詞法環(huán)境(變量 name 存在于其中)的引用。因此,當(dāng) myFunc 被調(diào)用時(shí),變量 name 仍然可用,其值 WebKaka 就被傳遞到log中。

相關(guān)文章

標(biāo)簽: 閉包  
x
  • 站長推薦
/* 左側(cè)顯示文章內(nèi)容目錄 */