摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數,也不是局部變量,所以是自由變量。
(關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導)
本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。
本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃,點擊查看前端進階的破冰之旅
如果覺得本系列不錯,歡迎轉發,您的支持就是我堅持的最大動力。
本期推薦文章JavaScript深入之閉包 ,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。
推薦理由本文是從作用域鏈的角度來介紹閉包,不同于上文圖解作用域和閉包,本文語言簡練,結構清晰,相比上文要容易理解些。建議搭配上文一起閱讀。
閱讀筆記紅寶書(p178)上對于閉包的定義:閉包是指有權訪問另外一個函數作用域中的變量的函數,
MDN 對閉包的定義為:閉包是指那些能夠訪問自由變量的函數。
其中自由變量,指在函數中使用的,但既不是函數參數arguments也不是函數的局部變量的變量,其實就是另外一個函數作用域中的變量。
使用上一篇文章的例子來說明下自由變量:【進階2-1期】深入淺出圖解作用域鏈和閉包
function getOuter(){ var date = "1127"; function getDate(str){ console.log(str + date); //訪問外部的date } return getDate("今天是:"); //"今天是:1127" } getOuter();
其中date既不是參數arguments,也不是局部變量,所以date是自由變量。
總結起來就是下面兩點:
1、是一個函數(比如,內部函數從父函數中返回)
2、能訪問上級函數作用域中的變量(哪怕上級函數上下文已經銷毀)
分析首先來一個簡單的例子
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); // foo指向函數f foo(); // 調用函數f()
簡要的執行過程如下:
進入全局代碼,創建全局執行上下文,全局執行上下文壓入執行上下文棧
全局執行上下文初始化
執行 checkscope 函數,創建 checkscope 函數執行上下文,checkscope 執行上下文被壓入執行上下文棧
checkscope 執行上下文初始化,創建變量對象、作用域鏈、this等
checkscope 函數執行完畢,checkscope 執行上下文從執行上下文棧中彈出
執行 f 函數,創建 f 函數執行上下文,f 執行上下文被壓入執行上下文棧
f 執行上下文初始化,創建變量對象、作用域鏈、this等
f 函數執行完畢,f 函數上下文從執行上下文棧中彈出
那么問題來了, 函數f 執行的時候,checkscope 函數上下文已經被銷毀了,那函數f是如何獲取到scope變量的呢?
上文(【進階2-1期】深入淺出圖解作用域鏈和閉包)介紹過,函數f 執行上下文維護了一個作用域鏈,會指向指向checkscope作用域,作用域鏈是一個數組,結構如下。
fContext = { Scope: [AO, checkscopeContext.AO, globalContext.VO], }
所以指向關系是當前作用域 --> checkscope作用域--> 全局作用域,即使 checkscopeContext 被銷毀了,但是 JavaScript 依然會讓 checkscopeContext.AO(活動對象) 活在內存中,f 函數依然可以通過 f 函數的作用域鏈找到它,這就是閉包實現的關鍵。
概念上面介紹的是實踐角度,其實閉包有很多種介紹,說法不一。
湯姆大叔翻譯的關于閉包的文章中的定義,ECMAScript中,閉包指的是:
1、從理論角度:所有的函數。因為它們都在創建的時候就將上層上下文的數據保存起來了。哪怕是簡單的全局變量也是如此,因為函數中訪問全局變量就相當于是在訪問自由變量,這個時候使用最外層的作用域。
2、從實踐角度:以下函數才算是閉包:
即使創建它的上下文已經銷毀,它仍然存在(比如,內部函數從父函數中返回)
在代碼中引用了自由變量
面試必刷題var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();
如果知道閉包的,答案就很明顯了,都是3
循環結束后,全局執行上下文的VO是
globalContext = { VO: { data: [...], i: 3 } }
執行 data[0] 函數的時候,data[0] 函數的作用域鏈為:
data[0]Context = { Scope: [AO, globalContext.VO] }
由于其自身沒有i變量,就會向上查找,所有從全局上下文查找到i為3,data[1] 和 data[2] 是一樣的。
改成閉包,方法就是data[i]返回一個函數,并訪問變量i
var data = []; for (var i = 0; i < 3; i++) { data[i] = (function (i) { return function(){ console.log(i); } })(i); } data[0](); // 0 data[1](); // 1 data[2](); // 2
循環結束后的全局執行上下文沒有變化。
執行 data[0] 函數的時候,data[0] 函數的作用域鏈發生了改變:
data[0]Context = { Scope: [AO, 匿名函數Context.AO, globalContext.VO] }
匿名函數執行上下文的AO為:
匿名函數Context = { AO: { arguments: { 0: 0, length: 1 }, i: 0 } }
因為閉包執行上下文中貯存了變量i,所以根據作用域鏈會在globalContext.VO中查找到變量i,并輸出0。
思考題上面必刷題改動一個地方,把for循環中的var i = 0,改成let i = 0。結果是什么,為什么???
var data = []; for (let i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();參考
JavaScript深入之閉包往期文章查看
【進階1-1期】理解JavaScript 中的執行上下文和執行棧
【進階1-2期】JavaScript深入之執行上下文棧和變量對象
【進階1-3期】JavaScript深入之內存空間詳細圖解
【進階1-4期】JavaScript深入之帶你走進內存機制
【進階1-5期】JavaScript深入之4類常見內存泄漏及如何避免
【進階2-1期】深入淺出圖解作用域鏈和閉包
每周計劃安排每周面試重難點計劃如下,如有修改會通知大家。每周一期,為期半年,準備明年跳槽的小伙伴們可以把本公眾號[置頂]()了。
【進階1期】 調用堆棧
【進階2期】 作用域閉包
【進階3期】 this全面解析
【進階4期】 深淺拷貝原理
【進階5期】 原型Prototype
【進階6期】 高階函數
【進階7期】 事件機制
【進階8期】 Event Loop原理
【進階9期】 Promise原理
【進階10期】Async/Await原理
【進階11期】防抖/節流原理
【進階12期】模塊化詳解
【進階13期】ES6重難點
【進階14期】計算機網絡概述
【進階15期】瀏覽器渲染原理
【進階16期】webpack配置
【進階17期】webpack原理
【進階18期】前端監控
【進階19期】跨域和安全
【進階20期】性能優化
【進階21期】VirtualDom原理
【進階22期】Diff算法
【進階23期】MVVM雙向綁定
【進階24期】Vuex原理
【進階25期】Redux原理
【進階26期】路由原理
【進階27期】VueRouter源碼解析
【進階28期】ReactRouter源碼解析
交流本人Github鏈接如下,歡迎各位Star
http://github.com/yygmind/blog
我是木易楊,網易高級前端工程師,跟著我每周重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!
如果你想加群討論每期面試知識點,公眾號回復[加群]即可
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99550.html
摘要:閉包面試題解由于作用域鏈機制的影響,閉包只能取得內部函數的最后一個值,這引起的一個副作用就是如果內部函數在一個循環中,那么變量的值始終為最后一個值。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...
摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對象,在全局環境中定義的變量就會綁定到全局對象中。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周開始前端進階的第二期,本周的主題是作用域閉包,今天是第6天。 本...
摘要:進階期理解中的執行上下文和執行棧進階期深入之執行上下文棧和變量對象但是今天補充一個知識點某些情況下,調用堆棧中函數調用的數量超出了調用堆棧的實際大小,瀏覽器會拋出一個錯誤終止運行。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題是調用堆棧,今天是第3天。 本計劃一共28期,每期重點攻...
摘要:引擎對堆內存中的對象進行分代管理新生代存活周期較短的對象,如臨時變量字符串等。內存泄漏對于持續運行的服務進程,必須及時釋放不再用到的內存。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題是調用堆棧,今天是第4天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃...
摘要:本期推薦文章類內存泄漏及如何避免,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。四種常見的內存泄漏劃重點這是個考點意外的全局變量未定義的變量會在全局對象創建一個新變量,如下。因為老版本的是無法檢測節點與代碼之間的循環引用,會導致內存泄漏。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題...
閱讀 1814·2021-10-20 13:49
閱讀 1356·2019-08-30 15:52
閱讀 2863·2019-08-29 16:37
閱讀 1033·2019-08-29 10:55
閱讀 3064·2019-08-26 12:14
閱讀 1649·2019-08-23 17:06
閱讀 3235·2019-08-23 16:59
閱讀 2543·2019-08-23 15:42