面試 | JS 閉包經(jīng)典使用場(chǎng)景和含閉包必刷題

思維導(dǎo)圖

閉包

了解閉包前先來了解一下上級(jí)作用域和堆棧內(nèi)存釋放問題。

上級(jí)作用域的概念

  • 函數(shù)的上級(jí)作用域在哪里創(chuàng)建創(chuàng)建的,上級(jí)作用域就是誰
var a = 10
function foo(){
    console.log(a)
}

function sum() {
    var a = 20
    foo()
}

sum()
/* 輸出
    10
/
復(fù)制代碼

函數(shù) foo() 是在全局下創(chuàng)建的廷粒,所以 a 的上級(jí)作用域就是 window扑毡,輸出就是 10

思考題

var n = 10
function fn(){
    var n =20
    function f() {
       n++;
       console.log(n)
     }
    f()
    return f
}

var x = fn()
x()
x()
console.log(n)
/* 輸出
*  21
    22
    23
    10
/
復(fù)制代碼

思路:fn 的返回值是什么變量 x 就是什么,這里 fn 的返回值是函數(shù)名 f 也就是 f 的堆內(nèi)存地址宴霸,x() 也就是執(zhí)行的是函數(shù) f()姆坚,而不是 fn()澳泵,輸出的結(jié)果顯而易見

  • 關(guān)于如何查找上級(jí)作用域

參考:徹底解決 JS 變量提升的面試題

JS 堆棧內(nèi)存釋放

  • 堆內(nèi)存:存儲(chǔ)引用類型值,對(duì)象類型就是鍵值對(duì)兼呵,函數(shù)就是代碼字符串兔辅。

  • 堆內(nèi)存釋放:將引用類型的空間地址變量賦值成 null,或沒有變量占用堆內(nèi)存了瀏覽器就會(huì)釋放掉這個(gè)地址

  • 棧內(nèi)存:提供代碼執(zhí)行的環(huán)境和存儲(chǔ)基本類型值击喂。

  • 棧內(nèi)存釋放:一般當(dāng)函數(shù)執(zhí)行完后函數(shù)的私有作用域就會(huì)被釋放掉维苔。

但棧內(nèi)存的釋放也有特殊情況:① 函數(shù)執(zhí)行完,但是函數(shù)的私有作用域內(nèi)有內(nèi)容被棧外的變量還在使用的懂昂,棧內(nèi)存就不能釋放里面的基本值也就不會(huì)被釋放蕉鸳。② 全局下的棧內(nèi)存只有頁面被關(guān)閉的時(shí)候才會(huì)被釋放

閉包是什么

在 JS 忍者秘籍(P90)中對(duì)閉包的定義:閉包允許函數(shù)訪問并操作函數(shù)外部的變量。紅寶書上對(duì)于閉包的定義:閉包是指有權(quán)訪問另外一個(gè)函數(shù)作用域中的變量的函數(shù)忍法。 MDN 對(duì)閉包的定義為:閉包是指那些能夠訪問自由變量的函數(shù)潮尝。這里的自由變量是外部函數(shù)作用域中的變量。

概述上面的話饿序,閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中變量的函數(shù)

形成閉包的原因

內(nèi)部的函數(shù)存在外部作用域的引用就會(huì)導(dǎo)致閉包勉失。從上面介紹的上級(jí)作用域的概念中其實(shí)就有閉包的例子 return f就是一個(gè)表現(xiàn)形式。

var a = 0
function foo(){
    var b =14
    function fo(){
        console.log(a, b)
    }
    fo()
}
foo()
復(fù)制代碼

這里的子函數(shù) fo 內(nèi)存就存在外部作用域的引用 a, b原探,所以這就會(huì)產(chǎn)生閉包

閉包的作用

  • 保護(hù)函數(shù)的私有變量不受外部的干擾乱凿。形成不銷毀的棧內(nèi)存。
  • 保存咽弦,把一些函數(shù)內(nèi)的值保存下來徒蟆。閉包可以實(shí)現(xiàn)方法和屬性的私有化

閉包經(jīng)典使用場(chǎng)景

    1. return 回一個(gè)函數(shù)
var n = 10
function fn(){
    var n =20
    function f() {
       n++;
       console.log(n)
     }
    return f
}

var x = fn()
x() // 21
復(fù)制代碼

這里的 return f, f()就是一個(gè)閉包,存在上級(jí)作用域的引用型型。

    1. 函數(shù)作為參數(shù)
var a = '林一一'
function foo(){
    var a = 'foo'
    function fo(){
        console.log(a)
    }
    return fo
}

function f(p){
    var a = 'f'
    p()
}
f(foo())
/* 輸出
*   foo
/ 
復(fù)制代碼

使用 return fo 返回回來段审,fo() 就是閉包,f(foo()) 執(zhí)行的參數(shù)就是函數(shù) fo闹蒜,因?yàn)?fo() 中的 a 的上級(jí)作用域就是函數(shù)foo()寺枉,所以輸出就是foo

    1. IIFE(自執(zhí)行函數(shù))
var n = '林一一';
(function p(){
    console.log(n)
})()
/* 輸出
*   林一一
/ 
復(fù)制代碼

同樣也是產(chǎn)生了閉包p(),存在 window下的引用 n绷落。

    1. 循環(huán)賦值
for(var i = 0; i<10; i++){
  (function(j){
       setTimeout(function(){
        console.log(j)
    }, 1000) 
  })(i)
}
復(fù)制代碼

因?yàn)榇嬖陂]包的原因上面能依次輸出1~10姥闪,閉包形成了10個(gè)互不干擾的私有作用域。將外層的自執(zhí)行函數(shù)去掉后就不存在外部作用域的引用了砌烁,輸出的結(jié)果就是連續(xù)的 10筐喳。為什么會(huì)連續(xù)輸出10,因?yàn)?JS 是單線程的遇到異步的代碼不會(huì)先執(zhí)行(會(huì)入棧)函喉,等到同步的代碼執(zhí)行完 i++ 到 10時(shí)避归,異步代碼才開始執(zhí)行此時(shí)的 i=10 輸出的都是 10。

    1. 使用回調(diào)函數(shù)就是在使用閉包
window.name = '林一一'
setTimeout(function timeHandler(){
  console.log(window.name);
}, 100)
復(fù)制代碼

使用閉包需要注意什么

容易導(dǎo)致內(nèi)存泄漏函似。閉包會(huì)攜帶包含其它的函數(shù)作用域槐脏,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過度使用閉包會(huì)導(dǎo)致內(nèi)存占用過多撇寞,所以要謹(jǐn)慎使用閉包顿天。

經(jīng)典面試題

  • for 循環(huán)和閉包(號(hào)稱必刷題)
var data = [];

for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]()
/* 輸出
    3
    3
    3
/
復(fù)制代碼

這里的 i 是全局下的 i,共用一個(gè)作用域蔑担,當(dāng)函數(shù)被執(zhí)行的時(shí)候這時(shí)的 i=3牌废,導(dǎo)致輸出的結(jié)構(gòu)都是3。

  • 使用閉包改善上面的寫法達(dá)到預(yù)期效果啤握,寫法1:自執(zhí)行函數(shù)和閉包
var data = [];

for (var i = 0; i < 3; i++) {
    (function(j){
      setTimeout( data[j] = function () {
        console.log(j);
      }, 0)
    })(i)
}

data[0]();
data[1]();
data[2]()
復(fù)制代碼
  • 寫法2:使用 let
var data = [];

for (let i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]()
復(fù)制代碼

let 具有塊級(jí)作用域鸟缕,形成的3個(gè)私有作用域都是互不干擾的。

參考

深入理解JavaScript閉包之什么是閉包

JavaScript深入之閉包

了解更多加入我們前端學(xué)習(xí)圈

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市懂从,隨后出現(xiàn)的幾起案子授段,更是在濱河造成了極大的恐慌,老刑警劉巖番甩,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侵贵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缘薛,警方通過查閱死者的電腦和手機(jī)窍育,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宴胧,“玉大人漱抓,你說我怎么就攤上這事∷∑耄” “怎么了乞娄?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)檐迟。 經(jīng)常有香客問我补胚,道長(zhǎng),這世上最難降的妖魔是什么追迟? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任溶其,我火速辦了婚禮,結(jié)果婚禮上敦间,老公的妹妹穿的比我還像新娘瓶逃。我一直安慰自己,他們只是感情好廓块,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布厢绝。 她就那樣靜靜地躺著,像睡著了一般带猴。 火紅的嫁衣襯著肌膚如雪昔汉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天拴清,我揣著相機(jī)與錄音靶病,去河邊找鬼。 笑死口予,一個(gè)胖子當(dāng)著我的面吹牛娄周,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沪停,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼煤辨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼裳涛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起众辨,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤端三,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后泻轰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體技肩,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年浮声,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旋奢。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泳挥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出至朗,到底是詐尸還是另有隱情屉符,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布锹引,位于F島的核電站矗钟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嫌变。R本人自食惡果不足惜吨艇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腾啥。 院中可真熱鬧东涡,春花似錦、人聲如沸倘待。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凸舵。三九已至祖娘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間啊奄,已是汗流浹背渐苏。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留增热,地道東北人整以。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像峻仇,于是被迫代替她去往敵國和親公黑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容