前言閉包是面試最喜歡問的一個問題了,,面試官最喜歡問: 1.什么是閉包,?2.閉包的作用是什么,? 什么是閉包?閉包(closure)是一種保護(hù)私有變量的機(jī)制,,在函數(shù)執(zhí)行時形成私有的作用域,,保護(hù)里面的私有變量不受外界干擾,即形成一個不銷毀的棧環(huán)境,。 閉包的特性: 先看一個典型的閉包, 實(shí)現(xiàn)計(jì)數(shù)器的功能 function myCounter() { var counter = 0;
function add() { counter += 1; console.log(counter); } return add; }
var myAdd = myCounter(); myAdd(); myAdd(); myAdd();// 計(jì)數(shù)器現(xiàn)在為 3
在弄清楚閉包之前,我們需要弄明白一個概念:私有的作用域 變量作用域var 聲明的變量有2個作用域:全局作用域 和局部作用域,。 let 和 const 聲明的變量有3個作用域:全局作用域 和局部作用域,, 還有個塊作用域。塊作用域這里就不講了 全局作用域在函數(shù)外部var 聲明的變量就是全局作用域 var count = 0
// 每次調(diào)用fun() count 加1 function fun() { count += 1; }
fun(); fun(); fun(); console.log(count); // 3
函數(shù)局部作用域在函數(shù)內(nèi)部聲明的變量是函數(shù)局部作用域 var count = 0
// 每次調(diào)用fun() count 加1 function fun() { var count = 0; count += 1; }
fun(); fun(); fun(); console.log(count); // 0
雖然函數(shù)內(nèi)部也聲明了一個count變量,,但是跟函數(shù)外部的count其實(shí)是2個不同的變量,,所以每次調(diào)用函數(shù),僅僅只是內(nèi)部變量count加1了,,但是外部的count還是0,。 計(jì)數(shù)器問題如果我們想設(shè)置一個計(jì)數(shù)器,當(dāng)我們打開一個頁面,,只有5次點(diǎn)擊機(jī)會,,點(diǎn)完就不能再點(diǎn)擊了。 <body> <button type="button" id="btn"></button> <script> var counter = 5;
// 每次調(diào)用fun() count -1 function fun() { counter -= 1; if (counter <= 0){ document.getElementById('btn').disabled = true; return }
}
// 設(shè)置初始值 document.getElementById('btn').innerHTML = '還剩' + counter + '次'; // 每點(diǎn)一次減1 document.getElementById('btn').onclick = function () { fun(); document.getElementById('btn').innerHTML = '還剩' + counter + '次'; } </script> </body>
實(shí)現(xiàn)效果
每點(diǎn)一次會減1
最后為0時不可被點(diǎn)擊 看起來是沒什么問題,,但是這個會有一個安全隱患,,由于counter變量是全局的,所以在其它任何地方都可以改變?nèi)肿兞康闹怠?br>如果用戶在控制臺,,改變了全局變量counter的值,,比如我在console執(zhí)行var counter=100 當(dāng)我點(diǎn)一下按鈕,就會變成99次
閉包的作用為了解決這種全局變量導(dǎo)致的問題,,所以我們需要在函數(shù)內(nèi)部設(shè)置一個私有變量 <body> <button type="button" id="btn"></button> <script> // 每次調(diào)用fun() count -1 function fun() { var counter = 5; document.getElementById('btn').innerHTML = '還剩' + counter + '次'; function inner(){ counter -= 1; if (counter <= 0){ document.getElementById('btn').disabled = true; } return counter } return inner } mycounter = fun(); // 每點(diǎn)一次減1 document.getElementById('btn').onclick = function () { var x = mycounter(); document.getElementById('btn').innerHTML = '還剩' + x + '次'; } </script> </body>
或者把counter作為一個變量傳給函數(shù),,此時也不會影響 <body> <button type="button" id="btn"></button> <script> // 每次調(diào)用fun() count -1 function fun(counter) { document.getElementById('btn').innerHTML = '還剩' + counter + '次'; function inner(){ counter -= 1; if (counter <= 0){ document.getElementById('btn').disabled = true; } return counter } return inner }
var counter = 5; mycounter = fun(counter);
// 每點(diǎn)一次減1 document.getElementById('btn').onclick = function () { var x = mycounter(); document.getElementById('btn').innerHTML = '還剩' + x + '次'; } </script> </body>
因?yàn)?fun()函數(shù)只執(zhí)行一次,所以會鎖定局部變量counter,。 計(jì)數(shù)器如果是從0開始,,每運(yùn)行一次函數(shù)加1,,那么可以優(yōu)化成以下代碼 <body> <button type="button" onclick="myFunction()">計(jì)數(shù)!</button> <p id="demo"></p> <script> // 自調(diào)用函數(shù) var add = (function () { var counter = 0; return function () {return counter += 1;} })();
function myFunction(){ document.getElementById("demo").innerHTML = add(); } </script> </body>
每點(diǎn)一次按鈕會加1
閉包的作用綜上所述,閉包的作用就是在外部a函數(shù)執(zhí)行后,,閉包使得Javascript的垃圾回收機(jī)制不會收回a所占用的資源,因?yàn)閍內(nèi)部函數(shù)的變量要給到內(nèi)部函數(shù)b繼續(xù)使用,。 那么閉包的好處有以下幾點(diǎn): 保護(hù)函數(shù)內(nèi)的變量安全 在內(nèi)存中維持一個變量(用的太多就變成了缺點(diǎn),,占內(nèi)存) ; 邏輯連續(xù),,當(dāng)閉包作為另一個函數(shù)調(diào)用的參數(shù)時,,避免你脫離當(dāng)前邏輯而單獨(dú)編寫額外邏輯。 方便調(diào)用上下文的局部變量,。 加強(qiáng)封裝性,,可以達(dá)到對變量的保護(hù)作用。
|