摘要:一概要紅寶書對于閉包的定義閉包就是有權(quán)訪問另外一個函數(shù)作用域中變量的函數(shù)。湯姆大叔在關(guān)于閉包對的文章的定義。解決辦法改成閉包,方法就是返回一個函數(shù),并且訪問變量循環(huán)結(jié)束后的全局執(zhí)行上下文沒有變化。
一、概要
紅寶書(P178)對于閉包的定義:閉包就是有權(quán)訪問另外一個函數(shù)作用域中變量的函數(shù)。
MDN,對于閉包的定義:閉包就是指能夠訪問自由變量的函數(shù)。
那么什么是自由變量?自由變量就是在函數(shù)中使用,但既不是函數(shù)參數(shù)arguments,也不是函數(shù)的局部變量的變量,就是說另外一個函數(shù)作用域中的變量。
閉包組成?閉包 = 函數(shù) + 函數(shù)能夠訪問的變量
文章首發(fā)地址于sau交流學習社區(qū):https://www.mwcxs.top/page/57...
二、分析舉個栗子:
var a = 1; function foo() { console.log(a); } foo();
foo函數(shù)可以訪問到變量a,但是a并不是foo函數(shù)的局部變量,也不是foo函數(shù)的參數(shù),所以a就是自由變量,那么函數(shù)foo+foo函數(shù)可以訪問自由變量a不就是構(gòu)成了一個閉包嘛。
我們再來看一個栗子:
function getOuter(){ var date = "1127"; function getDate(str){ console.log(str + date); //訪問外部的date } return getDate("今天是:"); //"今天是:1127" } getOuter();
其中date不是函數(shù)getDate的參數(shù),也不是局部變量,所以date是自由變量。
總結(jié)起來就是兩點:
1、是一個函數(shù)(比如:內(nèi)部函數(shù)從父函數(shù)中返回)
2、能夠訪問上級函數(shù)作用域中的變量(哪怕上級函數(shù)的上下文已經(jīng)銷毀)
然后我們再來看一個栗子(來自《JavaScript權(quán)威指南》)來分析:
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); // foo指向函數(shù)f foo();
這時候需要我們來分析一下這段代碼中執(zhí)行上下文棧和執(zhí)行上下文的變化情況。
簡要的分析一下執(zhí)行過程:
1、進入全局代碼,創(chuàng)建全局執(zhí)行上下文,全局執(zhí)行上下文壓入執(zhí)行上下文棧;
2、全局執(zhí)行上下文初始化;
3、執(zhí)行checkscope函數(shù),創(chuàng)建sheckscope函數(shù)執(zhí)行上下文,checkscope執(zhí)行上下文被壓入執(zhí)行上下文棧;
4、checkscope執(zhí)行上下文初始化,創(chuàng)建變量對象,作用域鏈,this等;
5、checkscope函數(shù)執(zhí)行完畢,checkscope執(zhí)行上下文從執(zhí)行上下文棧中彈出;
6、執(zhí)行f函數(shù),創(chuàng)建f函數(shù)執(zhí)行上下文,f執(zhí)行上下文壓入執(zhí)行上下文棧;
7、f執(zhí)行上下文初始化,創(chuàng)建變量對象,作用域鏈,this等;
8、f函數(shù)執(zhí)行完畢,f函數(shù)上下文從執(zhí)行上下文棧中彈出
那么問題來了,函數(shù)f執(zhí)行的時候,checkscope函數(shù)上下文已經(jīng)被銷毀了,那函數(shù)f是如何取到scope變量的?
答:函數(shù)f執(zhí)行上下文維護了一盒作用域鏈,作用域鏈會指向checkscope作用域,作用域鏈是一個數(shù)組,結(jié)構(gòu)如下:
fContext = { Scope: [AO, checkscopeContext.AO, globalContext.VO], }
所以指向關(guān)系:當前作用域-->checkscope作用域-->全局作用域,即使checkscopeContext被銷毀了,但是js依然會讓checkscopeContext.AO(活動對象)繼續(xù)存在內(nèi)存中,f函數(shù)依然可以通過f函數(shù)的作用域鏈找到它,這就是閉包的關(guān)鍵。
注:AO 表示活動對象,儲存了函數(shù)的參數(shù)、函數(shù)內(nèi)聲明的變量等
三、概念上面分析介紹的是閉包的實踐角度,其實閉包有很多種介紹,說法不一。
湯姆大叔在關(guān)于閉包對的文章的定義。ECMAScript中,閉包指的是:
1、從理論角度:所有的函數(shù),因為它們都是創(chuàng)建的時候就將上層上下文的數(shù)據(jù)保存起來了。哪怕是簡單的全局變量也是如此,因為函數(shù)中訪問全局變量就是相當于再訪問自由變量,這個時候使用最外層的作用域。
2、從實踐角度:以下函數(shù)才算閉包:
(1)即使創(chuàng)建它的上下文已經(jīng)摧毀了,它依然存在(比如,內(nèi)部函數(shù)中返回)
(2)在代碼中引用了自由變量
四、面試必刷的題var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();
如果知道是閉包,答案很明顯,都是3。
循環(huán)結(jié)束后,全局執(zhí)行上下文的VO是
globalContext = { VO: { data: [...], i: 3 } }
執(zhí)行data[0]函數(shù)的時候,data[0]函數(shù)的作用域鏈為:
data[0]Context = { Scope: [AO, globalContext.VO] }
由于其自身沒有i變量,就會向上查找,所有從全局上下文查找到i為3,data[1] 和 data[2] 是一樣的。
注:
1、for 循環(huán)不會創(chuàng)建一個執(zhí)行上下文,所有不會有 AO, i 的值是在全局對象的 AO 中,代碼初始的時候為:
globalContext = { VO: { data: [...], i: 0 } }
2、data[0] 是一個函數(shù)名,data[0]() 表示執(zhí)行這個函數(shù),當執(zhí)行函數(shù)的時候,循環(huán)已經(jīng)走完了;
3、函數(shù)能夠讀取到的值跟函數(shù)定義的位置有關(guān),跟執(zhí)行的位置無關(guān)。
解決辦法:
改成閉包,方法就是data[i]返回一個函數(shù),并且訪問變量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
循環(huán)結(jié)束后的全局執(zhí)行上下文沒有變化。
執(zhí)行 data[0] 函數(shù)的時候,data[0] 函數(shù)的作用域鏈發(fā)生了改變:
data[0]Context = { Scope: [AO, 匿名函數(shù)Context.AO, globalContext.VO] }
匿名函數(shù)執(zhí)行上下文的AO為:
匿名函數(shù)Context = { AO: { arguments: { 0: 0, length: 1 }, i: 0 } }
data[0]Context 的 AO 并沒有 i 值,所以會沿著作用域鏈從匿名函數(shù) Context.AO 中查找,這時候就會找 i 為 0,找到了就不會往 globalContext.VO 中查找了,即使 globalContext.VO 也有 i 的值(值為3),所以打印的結(jié)果就是0
五、思考題把for循環(huán)中的var i = 0,改成let i = 0。結(jié)果是什么,為什么?
var data = []; for (let i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();
或者這樣:
var data = []; var _loop = function _loop(i) { data[i] = function () { console.log(i); }; }; for (var i = 0; i < 3; i++) { _loop(i); } data[0](); data[1](); data[2]();六、參考
https://github.com/mqyqingfen...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109884.html
摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...
摘要:閉包面試題解由于作用域鏈機制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個值,這引起的一個副作用就是如果內(nèi)部函數(shù)在一個循環(huán)中,那么變量的值始終為最后一個值。 (關(guān)注福利,關(guān)注本公眾號回復[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...
摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來說只有當函數(shù)以返回值返回或者當函數(shù)以參數(shù)形式使用或者當函數(shù)中自由變量在函數(shù)外被引用時才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會問的問題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書籍等書中給出的概念,這些概念雖然...
摘要:一概要作用域和作用域鏈是中非常重要的特性,關(guān)系到理解整個體系,閉包是對作用域的延伸,其他語言也有閉包的特性。作用域鏈的作用他保證了變量對象的有序訪問。 一、概要 作用域和作用域鏈是js中非常重要的特性,關(guān)系到理解整個js體系,閉包是對作用域的延伸,其他語言也有閉包的特性。 那什么是作用域?作用域指的是一個變量和函數(shù)的作用范圍。 1、js中函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)始終是可見的; 2...
摘要:至此作用域鏈創(chuàng)建完畢。好了,通過深入理解作用域鏈,我們能跟好的理解的運行機制和閉包的原理。 前言 理解javascript中的作用域和作用域鏈對我們理解js這們語言。這次想深入的聊下關(guān)于js執(zhí)行的內(nèi)部機制,主要討論下,作用域,作用域鏈,閉包的概念。為了更好的理解這些東西,我模擬了當一個函數(shù)執(zhí)行時,js引擎做了哪些事情--那些我們看不見的動作。 關(guān)鍵詞: 執(zhí)行環(huán)境 作用域 作用域鏈 變...
閱讀 3660·2021-09-27 14:02
閱讀 1789·2019-08-30 15:56
閱讀 1744·2019-08-29 18:44
閱讀 3279·2019-08-29 17:21
閱讀 486·2019-08-26 17:15
閱讀 1176·2019-08-26 13:57
閱讀 1240·2019-08-26 13:56
閱讀 2880·2019-08-26 11:30