大名鼎鼎的閉包簇秒!面試必問溯泣。
請(qǐng)用自己的話簡(jiǎn)述
1.什么是「閉包」瞬女。
2.「閉包」的作用是什么窍帝。
首先來簡(jiǎn)述什么是閉包
假設(shè)上面三行代碼在一個(gè)立即執(zhí)行函數(shù)中(為簡(jiǎn)明起見,我就不寫立即執(zhí)行函數(shù)了诽偷,影響讀者理解)坤学。
評(píng)論里沒看完就說我寫得有問題的,請(qǐng)看清楚哦:
上面三行代碼在一個(gè)立即執(zhí)行函數(shù)中报慕。
三行代碼中深浮,有一個(gè)局部變量 local,有一個(gè)函數(shù) foo眠冈,foo 里面可以訪問到 local 變量飞苇。
好了這就是一個(gè)閉包:
「函數(shù)」和「函數(shù)內(nèi)部能訪問到的變量」(也叫環(huán)境)的總和,就是一個(gè)閉包蜗顽。
就這么簡(jiǎn)單布卡。
有的同學(xué)就疑惑了,閉包這么簡(jiǎn)單么雇盖?
「我聽說閉包是需要函數(shù)套函數(shù)忿等,然后 return 一個(gè)函數(shù)的呀!」
比如這樣:
這里面確實(shí)有閉包崔挖,local 變量和 bar 函數(shù)就組成了一個(gè)閉包(Closure)贸街。
為什么要函數(shù)套函數(shù)呢?
是因?yàn)樾枰植孔兞坷晗啵圆虐?local 放在一個(gè)函數(shù)里匾浪,如果不把 local 放在一個(gè)函數(shù)里,local 就是一個(gè)全局變量了卷哩,達(dá)不到使用閉包的目的——隱藏變量(等會(huì)會(huì)講)蛋辈。
這也是為什么我上面要說「運(yùn)行在一個(gè)立即執(zhí)行函數(shù)中」。
有些人看到「閉包」這個(gè)名字将谊,就一定覺得要用什么包起來才行冷溶。其實(shí)這是翻譯問題,閉包的原文是 Closure尊浓,跟「包」沒有任何關(guān)系逞频。
所以函數(shù)套函數(shù)只是為了造出一個(gè)局部變量,跟閉包無關(guān)栋齿。
為什么要 return bar 呢苗胀?
因?yàn)槿绻?return襟诸,你就無法使用這個(gè)閉包。把 return bar 改成 window.bar = bar 也是一樣的基协,只要讓外面可以訪問到這個(gè) bar 函數(shù)就行了歌亲。
所以 return bar 只是為了 bar 能被使用,也跟閉包無關(guān)澜驮。
閉包的作用
閉包常常用來「間接訪問一個(gè)變量」陷揪。換句話說,「隱藏一個(gè)變量」杂穷。
假設(shè)我們?cè)谧鲆粋€(gè)游戲悍缠,在寫其中關(guān)于「還剩幾條命」的代碼。
如果不用閉包耐量,你可以直接用一個(gè)全局變量:
這樣看起來很不妥飞蚓。萬一不小心把這個(gè)值改成 -1 了怎么辦。所以我們不能讓別人「直接訪問」這個(gè)變量廊蜒。怎么辦呢玷坠?
用局部變量。
但是用局部變量別人又訪問不到劲藐,怎么辦呢八堡?
暴露一個(gè)訪問器(函數(shù)),讓別人可以「間接訪問」聘芜。
代碼如下:
簡(jiǎn)明起見兄渺,我用了中文 :)
那么在其他的 JS 文件,就可以使用 window.獎(jiǎng)勵(lì)一條命() 來漲命汰现,使用 window.死一條命() 來讓角色掉一條命挂谍。
看到閉包在哪了嗎?
閉包到底是什么瞎饲?
第一句是變量聲明口叙,第二句是函數(shù)聲明,第三句是 console.log嗅战。
每一句我都學(xué)過妄田,為什么合起來我就看不出來是閉包?
我告訴你答案驮捍,你根本不需要知道閉包這個(gè)概念疟呐,一樣可以使用閉包!
閉包是 JS 函數(shù)作用域的副產(chǎn)品东且。
換句話說启具,正是由于 JS 的函數(shù)內(nèi)部可以使用函數(shù)外部的變量,所以這段代碼正好符合了閉包的定義珊泳。而不是 JS 故意要使用閉包鲁冯。
很多編程語言也支持閉包拷沸,另外有一些語言則不支持閉包。
只要你懂了 JS 的作用域薯演,你自然而然就懂了閉包撞芍,即使你不知道那就是閉包!
所謂閉包的作用
閉包會(huì)造成內(nèi)存泄露涣仿?
錯(cuò)。
說這話的人根本不知道什么是內(nèi)存泄露示惊。內(nèi)存泄露是指你用不到(訪問不到)的變量好港,依然占居著內(nèi)存空間,不能被再次利用起來米罚。
閉包里面的變量明明就是我們需要的變量(lives)钧汹,憑什么說是內(nèi)存泄露?
這個(gè)謠言是如何來的录择?
因?yàn)?IE拔莱。IE 有 bug,IE 在我們使用完閉包之后隘竭,依然回收不了閉包里面引用的變量塘秦。
這是 IE 的問題,不是閉包的問題动看。參見司徒正美的文章