摘要:然后最外層這個(gè)函數(shù)會(huì)返回一個(gè)新對(duì)象,對(duì)象里面有一個(gè)屬性,名為,而這個(gè)屬性的值是一個(gè)匿名函數(shù),它會(huì)返回。
最近看到一條有意思的閉包面試題,但是看到原文的解析,我自己覺得有點(diǎn)迷糊,所以自己重新做一下這條題目。
閉包面試題原題function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) { // ③ return fun(m, n); // ④ } }; } // 第一個(gè)例子 var a = fun(0); // 返回undefined a.fun(1); // 返回 ? a.fun(2); // 返回 ? a.fun(3); // 返回 ? // 第二個(gè)例子 var b = fun(0) .fun(1) .fun(2) .fun(3); //undefined,?,?,? // 第三個(gè)例子 var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined,?,?,?一、關(guān)于這個(gè)函數(shù)的執(zhí)行過程
先大致說一下這個(gè)函數(shù)的執(zhí)行過程:
① 初始化一個(gè)具名函數(shù),具名函數(shù)就是有名字的函數(shù),名字叫 fun。
② 第一個(gè) fun 具名函數(shù)執(zhí)行之后會(huì)返回一個(gè)對(duì)象字面量表達(dá)式,即返回一個(gè)新的object對(duì)象。
{ // 這是一個(gè)對(duì)象,這是對(duì)象字面量表達(dá)式創(chuàng)建對(duì)象的寫法,例如{a:11,b:22} fun: function(m) { return fun(m, n); } }
③ 返回的對(duì)象里面含有fun這個(gè)屬性,并且這個(gè)屬性里面存放的是一個(gè)新創(chuàng)建匿名函數(shù)表達(dá)式function(m) {}。
④ 在③里面創(chuàng)建的匿名函數(shù)會(huì)返回一個(gè)叫 fun 的具名函數(shù)return fun(m, n);,這里需要說明一下這個(gè) fun 函數(shù)返回之后的執(zhí)行過程:
1. 返回 fun 函數(shù),但默認(rèn)不執(zhí)行,因?yàn)樵?js 里面,函數(shù)是可以保存在變量里面的。 2. 如果想要執(zhí)行 fun 函數(shù),那么首先會(huì)在當(dāng)前作用域?qū)ふ医衒un 名字的具名函數(shù),但是因?yàn)楫?dāng)前作用域里 fun 名字的函數(shù)是沒有被定義的,所以會(huì)自動(dòng)往上一級(jí)查找。 2.1 注解:當(dāng)前的作用域里是一個(gè)新創(chuàng)建的對(duì)象,并且對(duì)象里面只有 fun 屬性,而沒有 fun 具名函數(shù) 2.2 注解:js 作用域鏈的問題,會(huì)導(dǎo)致他會(huì)不斷地往上級(jí)鏈查找。 3. 在當(dāng)前作用域沒找到,所以一直往上層找,直到找到了頂層的 fun函數(shù),然后執(zhí)行這個(gè)頂層的 fun 函數(shù)。 4. 然后這兩個(gè) fun 函數(shù)會(huì)形成閉包,第二個(gè) fun 函數(shù)會(huì)不斷引用第一個(gè) fun 函數(shù),從而導(dǎo)致一些局部變量例如 n,o 得以保存。
所謂閉包:各種解釋都有,但都不是很接地氣,簡(jiǎn)單的來說就是在 js 里面,有一些變量(內(nèi)存)可以被不斷的引用,導(dǎo)致了變量(內(nèi)存)沒有被釋放和回收,從而形成了一個(gè)獨(dú)立的存在,這里涉及了js 的作用域鏈和 js 回收機(jī)制,結(jié)合兩者來理解就可以了。二、第一個(gè)例子的輸出結(jié)果分析 1. var a = fun(0); // 返回 undefined
注解:
因?yàn)樽钔鈱拥膄un 函數(shù)fun(n, o)是有2個(gè)參數(shù)的,如果第二個(gè)參數(shù)沒有傳,那么默認(rèn)就會(huì)被轉(zhuǎn)換為 undefined,所以執(zhí)行之后輸出 undefined,因?yàn)?console.log 輸出的是o console.log(o);。
然后最外層這個(gè) fun 函數(shù)會(huì)返回一個(gè)新對(duì)象,對(duì)象里面有一個(gè)屬性,名為 fun,而這個(gè)fun 屬性的值是一個(gè)匿名函數(shù),它會(huì)返回fun(m, n); 。
function fun(n, o) { // ① console.log(o); // 這里首先輸出了 n 的值為undefined return { // ② fun: function(m) { // ③ return fun(m, n); // ④ } }; }2. a.fun(1); // 返回 0
注解:
由于之前運(yùn)行了var a = fun(0);,返回了一個(gè)對(duì)象,并且賦值給了變量a,所以 a 是可以訪問對(duì)象里面的屬性的,例如a.fun。
a.fun(1);這里意思是:
訪問 a 對(duì)象的 fun 屬性,因?yàn)閍 的 fun 屬性的值保存的是一個(gè)匿名函數(shù)③,所以要使用的話需要加上()。
a.fun() 實(shí)際上調(diào)用的是 fun 屬性里面的匿名函數(shù),由于匿名函數(shù)返回的fun(m, n); 無法在當(dāng)前作用域找到(因?yàn)楫?dāng)前作用域沒有這個(gè)定義這個(gè)函數(shù)),所以會(huì)往上找,找到了頂層的函數(shù)fun(n, o),這樣就會(huì)出現(xiàn)閉包的狀態(tài),頂層的fun 函數(shù)被內(nèi)層的 fun 函數(shù)引用,之前①的fun(0)的0被保存下來了,作為 n 參數(shù)的值。
a.fun(1)這里傳入了第一個(gè)參數(shù)1,所以就是 m=1,(因?yàn)棰劢邮找粋€(gè)參數(shù))。
所以④的fun(m,n)就會(huì)是fun(1,0),所以輸出0
// 已經(jīng)執(zhí)行過一次var a = fun(0) function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) { // ③ m=1 return fun(m, n); // ④ 不斷引用①,閉包生成,①的n 的值被保存為0 } }; }3. a.fun(2); // 返回 0
注解:
這里傳入一個(gè)參數(shù),參數(shù)的值為2,跟上面的a.fun(1);是一樣的流程執(zhí)行。
最終是 fun(2,0)執(zhí)行,那么輸出 o 就是0了
function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) { // ③ return fun(m, n); // ④ } }; }4. a.fun(3); // 返回 0
跟上面雷同,所以不做解釋了。
二、第二個(gè)例子的輸出結(jié)果分析第二個(gè)例子其實(shí)是一個(gè)語(yǔ)句,只是進(jìn)行了鏈?zhǔn)秸{(diào)用,所以會(huì)有一些不一樣的處理。1. 第一個(gè)返回 undefined
var b = fun(0) // 返回 undefined
注解:
第一個(gè)返回 undefined 毋容置疑了,所以不說。
2. 第二個(gè)返回 0fun(0).fun(1) // 返回 0
注解:
執(zhí)行fun(0)的時(shí)候返回了一個(gè)對(duì)象,對(duì)象里面有 fun 屬性,而這個(gè) fun 屬性的值是一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)會(huì)返回一個(gè) fun 函數(shù)。
當(dāng)執(zhí)行完fun(0)后,再鏈?zhǔn)街苯訄?zhí)行.fun(1)的時(shí)候,它是會(huì)調(diào)用前者返回的對(duì)象里的 fun 屬性,并且傳入了1作為第一個(gè)參數(shù),即m=1,并且返回的 fun 函數(shù)跟前者形成閉包,會(huì)不斷引用前者,所以 n=0 也被保存下來了。
所以最終執(zhí)行的時(shí)候是fun(m, n)即 fun(1,0),所以返回0
3. 第三個(gè)返回1fun(0).fun(1).fun(2)
注解:
執(zhí)行fun(0)的時(shí)候返回了一個(gè)對(duì)象,對(duì)象里面有 fun 屬性,而這個(gè) fun 屬性的值是一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)會(huì)返回一個(gè) fun 函數(shù)。
當(dāng)執(zhí)行完fun(0)后,再鏈?zhǔn)街苯訄?zhí)行.fun(1)的時(shí)候,它是會(huì)調(diào)用前者返回的對(duì)象里的 fun 屬性,并且傳入了1作為第一個(gè)參數(shù),即m=1,并且返回的 fun 函數(shù)跟前者形成閉包,會(huì)不斷引用前者,所以 n=0 也被保存下來了。
當(dāng)再次鏈?zhǔn)街苯訄?zhí)行.fun(2)的時(shí)候,這里使用的閉包是.fun(1)返回的閉包,因?yàn)槊看螆?zhí)行 fun 函數(shù)都會(huì)返回一個(gè)新對(duì)象,而.fun(2)引用的是.fun(1),所以 n 的值被保留為1
.fun(2)返回的是fun(m, n),而這里會(huì)跟.fun(1)(即fun(1, o))形成閉包,所以1為 n 的值被保留。
需要注意的是,js 作用域鏈只要找到可以使用的,就會(huì)馬上停止向上搜索,所以.fun(2)找到.fun(1)就馬上停止向上搜索了,所以引用的是.fun(1)的值。
4. 第四個(gè)返回是2跟第三個(gè)返回類似,所以不做解釋了。
第三個(gè)例子的輸出結(jié)果分析// 這里已經(jīng)無需多說了,跟第二個(gè)例子類似。 var c = fun(0).fun(1); // 返回 undefined 和01. 第三個(gè)返回是1,第四個(gè)返回是1
c.fun(2); // 第三個(gè)返回 1 c.fun(3); // 第四個(gè)返回 1
注解:
基于第一個(gè)返回和第二個(gè)返回,n 已經(jīng)被賦值為1了。
然后這里雖然多次執(zhí)行了 fun 函數(shù),但是因?yàn)闆]有再次形成閉包,n 的值沒有再次被改變,所以一直保持著1.
為了避免原文被吃掉,所以我這里保留了截圖,并且加了一篇解釋 js 閉包還不錯(cuò)的文章作為參考使用。
大部分人都會(huì)做錯(cuò)的經(jīng)典JS閉包面試題.pdf
JavaScript 的閉包原理與詳解 - CSDN博客.pdf
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/108345.html
摘要:函數(shù)柯里化在函數(shù)式編程中,函數(shù)是一等公民。函數(shù)柯里化的主要作用和特點(diǎn)就是參數(shù)復(fù)用提前返回和延遲執(zhí)行??赡茉趯?shí)際應(yīng)用場(chǎng)景中,很少使用函數(shù)柯里化的解決方案,但是了解認(rèn)識(shí)函數(shù)柯里化對(duì)自身的提升還是有幫助的。 最近在整理面試資源的時(shí)候,發(fā)現(xiàn)一道有意思的題目,所以就記錄下來。 題目 如何實(shí)現(xiàn) multi(2)(3)(4)=24? 首先來分析下這道題,實(shí)現(xiàn)一個(gè) multi 函數(shù)并依次傳入?yún)?shù)執(zhí)行,...
摘要:忍者秘籍一書中,對(duì)于柯里化的定義如下在一個(gè)函數(shù)中首先填充幾個(gè)參數(shù)然后再返回一個(gè)新函數(shù)的技術(shù)稱為柯里化?;氐轿覀兊念}目本身,其實(shí)根據(jù)測(cè)試用例我們可以發(fā)現(xiàn),函數(shù)的要求就是接受單一函數(shù),例如但是與柯里化不同之處在于,柯里化返回的一個(gè)新函數(shù)。 歡迎大家再一次來到我的文章專欄:從面試題中我們能學(xué)到什么,各位同行小伙伴是否已經(jīng)開始了悠閑的春節(jié)假期呢?在這里提前祝大家雞年大吉吧~哈哈,之前有人說...
摘要:閉包有多重前端知識(shí)點(diǎn)大百科全書前端掘金,,技巧使你的更加專業(yè)前端掘金一個(gè)幫你提升技巧的收藏集。 Vue全家桶實(shí)現(xiàn)還原豆瓣電影wap版 - 掘金用vue全家桶仿寫豆瓣電影wap版。 最近在公司項(xiàng)目中嘗試使用vue,但奈何自己初學(xué)水平有限,上了vue沒有上vuex,開發(fā)過程特別難受。 于是玩一玩本項(xiàng)目,算是對(duì)相關(guān)技術(shù)更加熟悉了。 原計(jì)劃仿寫完所有頁(yè)面,礙于豆瓣的接口API有限,實(shí)現(xiàn)頁(yè)面也有...
摘要:函數(shù)是這樣子聲明的使用了系統(tǒng)自己的構(gòu)造函數(shù)來聲明,第一個(gè)參數(shù)是,函數(shù)體內(nèi)又。構(gòu)造函數(shù)調(diào)用情況正常方式調(diào)用無窮無盡當(dāng)然,里還歸納了幾項(xiàng)比較簡(jiǎn)單,我就不再翻譯了。 上一篇從一道面試題,到我可能看了假源碼中,由淺入深介紹了關(guān)于一篇經(jīng)典面試題的解法。最后在皆大歡喜的結(jié)尾中,突生變化,懸念又起。這一篇,就是為了解開這個(gè)懸念。 如果你還沒有看過前傳,可以參看前情回顧: 回顧1. 題目是模擬實(shí)現(xiàn)ES...
摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對(duì)象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。同時(shí),將第一個(gè)參數(shù)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的顆粒化基礎(chǔ)。 今天想談?wù)勔坏狼岸嗣嬖囶},我做面試官的時(shí)候經(jīng)常喜歡用它來考察面試者的基礎(chǔ)是否扎實(shí),以及邏輯、思維能力和臨場(chǎng)表現(xiàn),題目是:模擬實(shí)現(xiàn)ES5中原生bind函數(shù)。也許這道題目已經(jīng)不再新鮮,部分讀者也會(huì)有思路來解答。社區(qū)上關(guān)于原生bind的研...
閱讀 1405·2023-04-26 00:10
閱讀 2494·2021-09-22 15:38
閱讀 4119·2021-09-22 15:13
閱讀 3608·2019-08-30 13:11
閱讀 704·2019-08-30 11:01
閱讀 3106·2019-08-29 14:20
閱讀 3271·2019-08-29 13:27
閱讀 1786·2019-08-29 11:33