摘要:環(huán)境由閉包創(chuàng)建時在作用域中的任何局部變量組成。嚴(yán)格來說,閉包需要滿足三個條件訪問所在作用域函數(shù)嵌套在所在作用域外被調(diào)用閉包的形成原理先了解的垃圾回收機制會找出不再使用的變量,不再使用意味著這個變量生命周期的結(jié)束。
什么是閉包 最原始定義
閉包(closure),是指函數(shù)變量可以保存在函數(shù)作用域內(nèi),因此看起來是函數(shù)將變量“包裹”了起來。
//根據(jù)定義,包含變量的函數(shù)就是閉包 function foo() { var a = 0; } cosole.log(a) // Uncaught ReferenceError: a is not defined《JavaScript高級程序設(shè)計》對閉包定義
閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)
//訪問上層函數(shù)的作用域的內(nèi)層函數(shù)就是閉包 function foo() { var a = 2; function bar() { console.log(a); } bar(); } foo();《JavaScript權(quán)威指南》對閉包定義
函數(shù)對象可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)體內(nèi)部變量可以保存在函數(shù)作用域內(nèi),這就是閉包。
var global = "global scope"; //全局變量 function checkscope() { var scope = "local scope"; //局部變量 function f() { return scope; //在作用域中返回這個值 }; return f(); } checkscope(); // 返回 "local scope"《你不知道的JavaScript》這樣描述
當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時,就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行。
//fn3就是fn2函數(shù)本身。執(zhí)行fn3能正常輸出name //這不就是fn2能記住并訪問它所在的詞法作用域,而且fn2函數(shù)的運行還是在當(dāng)前詞法作用域之外了。 function fn1() { var name = "iceman"; function fn2() { console.log(name); } return fn2; } var fn3 = fn1(); fn3();MDN 上面這么說:
閉包是一種特殊的對象。它由兩部分構(gòu)成:函數(shù),以及創(chuàng)建該函數(shù)的環(huán)境。環(huán)境由閉包創(chuàng)建時在作用域中的任何局部變量組成。
簡單說就是指那些能夠訪問自由變量的函數(shù)。
【1】訪問所在作用域;
【2】函數(shù)嵌套;
【3】在所在作用域外被調(diào)用
Javascript 會找出不再使用的變量,不再使用意味著這個變量生命周期的結(jié)束。
Javascript 中存在兩種變量——全局變量和局部變量,全部變量的聲明周期會一直持續(xù),直到頁面卸載而局部變量聲明在函數(shù)中,它的聲明周期從執(zhí)行函數(shù)開始,直到函數(shù)執(zhí)行結(jié)束。在這個過程中,局部變量會在堆或棧上被分配相應(yīng)的空間以存儲它們的值,函數(shù)執(zhí)行結(jié)束,這些局部變量也不再被使用,它們所占用的空間也就被釋放。
但是有一種情況的局部變量不會隨著函數(shù)的結(jié)束而被回收,那就是局部變量被函數(shù)外部的變量所使用,其中一種情況就是閉包,因為在函數(shù)執(zhí)行結(jié)束后,函數(shù)外部的變量依然指向函數(shù)內(nèi)的局部變量,此時的局部變量依然在被使用,所以也就不能夠被回收
var scope = "global scope"; function checkScope() { var scope = "local scope"; return function() { console.log(scope); } } var result = checkScope(); result(); // local scope checkScope變量對象中的scope,非全局變量scope
此匿名函數(shù)的作用域鏈包括checkScope的活動對象和全局變量對象, 當(dāng)checkScope函數(shù)執(zhí)行完畢后,checkScope的活動對象并不會被銷毀,因為匿名函數(shù)的作用域鏈還在引用checkScope的活動對象,也就是checkScope的執(zhí)行環(huán)境被銷毀,但是其活動對象沒有被銷毀,留存在堆內(nèi)存中,直到匿名函數(shù)銷毀后,checkScope的活動對象才會銷毀
從作用域鏈理解閉包的形成從理論角度:所有的函數(shù)。因為它們都在創(chuàng)建的時候就將上層上下文的數(shù)據(jù)保存起來了。哪怕是簡單的全局變量也是如此,因為函數(shù)中訪問全局變量就相當(dāng)于是在訪問自由變量,這個時候使用最外層的作用域。
從實踐角度:以下函數(shù)才算是閉包:
i. 即使創(chuàng)建它的上下文已經(jīng)銷毀,它仍然存在(比如,內(nèi)部函數(shù)從父函數(shù)中返回)
ii. 在代碼中引用了自由變量
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); foo(); fContext = {//f函數(shù)的執(zhí)行上下文 Scope: [AO, checkscopeContext.AO, globalContext.VO], }
對的,就是因為這個作用域鏈,f函數(shù)在聲明的時候壓入了上層的變量對象,f 函數(shù)依然可以讀取到 checkscopeContext.AO 的值,并且如果當(dāng) f 函數(shù)引用了 checkscopeContext.AO 中的值的時候,即使 checkscopeContext 被銷毀了,但是 JavaScript 依然會讓 checkscopeContext.AO 活在內(nèi)存中(和垃圾回收機制有關(guān)下文會說),f 函數(shù)依然可以通過 f 函數(shù)的作用域鏈找到它,正是因為 JavaScript 做到了這一點,從而實現(xiàn)了閉包這個概念。
閉包的作用-模仿塊級作用域,封裝私有變量任何在函數(shù)中定義的變量,都可以認為是私有變量,因為不能在函數(shù)外部訪問這些變量。
私有變量包括函數(shù)的參數(shù)、局部變量和函數(shù)內(nèi)定義的其他函數(shù)。
function module() { var arr = []; function add(val) { if (typeof val == "number") { arr.push(val); } } function get(index) { if (index < arr.length) { return arr[index] } else { return null; } } return { add: add, get: get } } var mod1 = module(); mod1.add(1); mod1.add(2); mod1.add("xxx"); console.log(mod1.get(2));//外部是無法直接拿到arr的只能通過get來拿閉包的作用-使變量保存在內(nèi)存中不被銷毀 實例1-計數(shù)器
我們來實現(xiàn)一個計數(shù)器,每調(diào)用一次計數(shù)器返回值加一:
var counter = 0; function add() { return counter += 1; } add(); add(); add();// 計數(shù)器現(xiàn)在為 3
問題:
全局變量容易被其他代碼改變
如果我需要同時用兩個計數(shù)器,但這種寫法只能滿足一個使用,另一個還想用的話就要再寫個counter2函數(shù),再定義一個counter2的全局變量。
那我們把counter放在add函數(shù)里面不就好了么?
function add() { var counter = 0; return counter += 1; } add(); add(); add();// 本意是想輸出 3, 但輸出的都是 1
所以這樣做的話,每次調(diào)用add函數(shù),counter的值都要被初始化為0,還是達不到我們的目的。
使用閉包來寫就會解決這些問題
function add() { var index = 1; function counter() { return index ++; } return counter; } // test var addA = add() ; var addB = add() ; addA(); // 1 addA(); // 2 addB(); // 1 addB(); // 2實例2-延時打印
這樣打印出來的全部都是10,原因是for循環(huán)是同步的會在延時1000毫秒的過程中一直執(zhí)行
等function執(zhí)行的時候變量i指向的是同一個內(nèi)存地址,且值已經(jīng)變成的10
for (var i = 1; i <= 10; i++) { setTimeout(function () { console.log(i); }, 1000); }
改進,用自執(zhí)行的函數(shù)創(chuàng)建簡單的閉包,讓每一次for循環(huán)的i都在不同的內(nèi)存地址中且不被銷毀
for (var i = 1; i <= 10; i++) { (function () { var j = i; setTimeout(function () { console.log(j); }, 1000); })(); }
優(yōu)化寫法
for (var i = 1; i <= 10; i++) { (function (j) { setTimeout(function () { console.log(j); }, 1000); })(i); }聯(lián)系Static靜態(tài)變量
閉包的作用主要就是讓變量的值始終保持在內(nèi)存中。
C++或C語言還有Java中都有static靜態(tài)變量也是讓變量始終保存在內(nèi)存中。
這樣來看好像閉包好像有點static靜態(tài)變量的意思。
閉包就是子函數(shù)可以有權(quán)訪問父函數(shù)的變量、父函數(shù)的父函數(shù)的變量、一直到全局變量。
歸根結(jié)底,就是利用js得詞法(靜態(tài))作用域,即作用域鏈在函數(shù)創(chuàng)建的時候就確定了。
子函數(shù)如果不被銷毀,整條作用域鏈上的變量仍然保存在內(nèi)存中,這樣就形成了閉包
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/106523.html
摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當(dāng)作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統(tǒng)的類繼承還要強大。中文指南基本操作指南二繼續(xù)熟悉的幾對方法,包括,,。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...
摘要:總結(jié)上面的大部分方式都可以互相組合使用的,一般來說如果要設(shè)計系統(tǒng),可能會用到松耦合擴展,私有狀態(tài)和子模塊這樣的方式。 簡介 Module模式是JavaScript編程中一個非常通用的模式,一般情況下,大家都知道基本用法,本文嘗試著給大家更多該模式的高級使用方式。 首先我們來看看Module模式的基本特征: 模塊化,可重用 封裝了變量和function,和全局的namaspace不接觸...
摘要:中的的線程是以事件循環(huán)和消息隊列的形式存在,包含兩個任務(wù)隊列,一個是內(nèi)部隊列,一個是外部隊列,而的優(yōu)先級又高于。同時還有處理按住時的事件額外處理,同時手勢處理一般在的子類進行。谷歌大會之后,有不少人咨詢了我 Flutter 相關(guān)的問題,其中有不少是和面試相關(guān)的,如今一些招聘上也開始羅列 Flutter 相關(guān)要求,最后想了想還是寫一期總結(jié)吧,也算是 Flutter 的階段復(fù)習(xí)。 ??系統(tǒng)完...
摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...
摘要:首次運行代碼時,會創(chuàng)建一個全局執(zhí)行上下文并到當(dāng)前的執(zhí)行棧中。執(zhí)行上下文的創(chuàng)建執(zhí)行上下文分兩個階段創(chuàng)建創(chuàng)建階段執(zhí)行階段創(chuàng)建階段確定的值,也被稱為。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第一期,本周的主題是調(diào)用堆棧,,今天是第一天 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進...
閱讀 3070·2021-11-22 13:54
閱讀 834·2021-11-04 16:08
閱讀 4460·2021-10-11 11:09
閱讀 3597·2021-09-22 16:05
閱讀 910·2019-08-30 15:54
閱讀 386·2019-08-30 15:44
閱讀 593·2019-08-30 14:05
閱讀 1013·2019-08-30 12:46