摘要:在此例中,在匿名函數(shù)被返回后,它的作用域鏈初始化為包含函數(shù)的活動對象和全局變量對象。函數(shù)在執(zhí)行完畢后,其活動對象也不會被銷毀,因為匿名函數(shù)的作用域鏈仍然在引用這個活動對象,結(jié)果就是只是的執(zhí)行環(huán)境的作用域鏈會被銷毀,其活動對象會留在內(nèi)存中。
寫在前面
注:這個系列是本人對js知識的一些梳理,其中不少內(nèi)容來自書籍:Javascript高級程序設(shè)計第三版和JavaScript權(quán)威指南第六版,感謝它們的作者和譯者。有發(fā)現(xiàn)什么問題的,歡迎留言指出。
1.執(zhí)行環(huán)境執(zhí)行環(huán)境簡稱“環(huán)境”,定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)。每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。
全局執(zhí)行環(huán)境是最外圍的一個執(zhí)行環(huán)境。在Web瀏覽器中,全局執(zhí)行環(huán)境被認(rèn)為是window對象,因此所有全局變量和函數(shù)都是作為window對象的屬性和方法創(chuàng)建的。
某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢,環(huán)境被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀。
執(zhí)行流:每個函數(shù)都有自己的執(zhí)行環(huán)境,當(dāng)執(zhí)行流進(jìn)入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。
代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈(scope chain),用途是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象。如果這個環(huán)境是函數(shù),則將其活動對象作為變量對象?;顒訉ο笤谧铋_始時只包含arguments 對象。作用域鏈的下一個變量對象來自包含環(huán)境,而再下一個變量對象則來自下一個包含環(huán)境。這樣,一直延續(xù)到全局執(zhí)行環(huán)境;全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈中的最后一個對象:
var color = "blue"; function changeColor() { if(color == "blue"){ color = "red"; }else{ color = "blue"; } } changeColor(); console.log("color is:" + color);//red
例子中,函數(shù)changeColor()的作用域鏈包含兩個對象:它自己的變量對象(其中定義著arguments對象)和全局環(huán)境的變量對象??梢栽诤瘮?shù)內(nèi)部訪問變量 color,就是因為可以在作用域鏈中找到它。
var color = "blue"; function changeColor() { var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; //這里可以訪問color、anotherColor和tempColor } //這里可以訪問color和anotherColor,但不能訪問tempColor swapColors(); } // 這里只能訪問color changeColor();
以上共涉及3個執(zhí)行環(huán)境:全局環(huán)境、changeColor()的局部環(huán)境和swapColors()的局部環(huán)境。顯然,內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。
2.閉包閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式,就是在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù)。
function createComparisonFunction(propertyName) { return function (object1, object2) { var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1value2){ return 1; }else{ return 0; } } } //創(chuàng)建函數(shù) var compareNames = createComparisonFunction("name"); //調(diào)用函數(shù) var result = compareNames({name:"jaychou"},{name:"xiaoming"}); //解除對匿名函數(shù)的引用(釋放內(nèi)存) compareNames = null;
以上過程:
1.定義函數(shù)內(nèi)部的函數(shù)時,會將它的包含函數(shù)的活動對象添加到它的作用域鏈中。在此例中,在匿名函數(shù)被返回后,它的作用域鏈初始化為包含createComparisonFunction()函數(shù)的活動對象和全局變量對象。
2.createComparisonFunction()函數(shù)在執(zhí)行完畢后,其活動對象也不會被銷毀,因為匿名函數(shù)的作用域鏈仍然在引用這個活動對象,結(jié)果就是只是createComparisonFunction()的執(zhí)行環(huán)境的作用域鏈會被銷毀,其活動對象會留在內(nèi)存中。
3.直到代碼中將匿名函數(shù)置為null釋放內(nèi)存后,createComparisonFunction()的活動對象才會被銷毀。
注意:由于閉包會攜帶包含它的函數(shù)的作用域,所以會比其他函數(shù)占用更多的內(nèi)存,過多使用閉包會導(dǎo)致內(nèi)存占用過多,所以在很有必要時才考慮使用閉包。
3.閉包的最常見問題function createFunctions() { var result = new Array(); for(var i=0;i<10;i++){ result[i] = function () { return i; }; } return result; } console.log(createFunctions()[4]());//會打印10
數(shù)組里面的每個函數(shù)都是打印10,因為每個函數(shù)的作用域鏈中都保存著createFunctions()函數(shù)的活動對象,所以他們引用的都是同一個變量i。當(dāng)函數(shù)數(shù)組被返回時,變量i的值是10,所以就是上面的結(jié)果了。通過創(chuàng)建另一個匿名函數(shù)的改造如下符合預(yù)期:
function createFunctions() { var result = new Array(); for(var i=0;i<10;i++){ result[i] = function (num) { return function () { return num; } }(i); } return result; } console.log(createFunctions()[4]());//會打印4
沒有直接把閉包賦值給數(shù)組,而是定義了一個匿名函數(shù),并將立即執(zhí)行該匿名函數(shù)的結(jié)果賦給函數(shù),函數(shù)參數(shù)是按值傳遞的,所以會把變量i的當(dāng)前值復(fù)制給參數(shù)num。
在這個立即執(zhí)行函數(shù)里面創(chuàng)建并返回了一個訪問num的閉包。這個閉包被返回時都準(zhǔn)確包含了對應(yīng)的包含環(huán)境的活動對象的num值。
4.閉包的常見作用給構(gòu)造函數(shù)創(chuàng)建私用變量和私有函數(shù),并定義特權(quán)方法;
創(chuàng)建單例,在函數(shù)最后返回公用方法
等等
總體而言,閉包對于我們理解執(zhí)行環(huán)境,理解作用域鏈很有幫助,但平常如果不是很有必要就不要用,占用內(nèi)存比較多。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/108379.html
摘要:好程序員前端培訓(xùn)入門之基礎(chǔ)知識梳理匯總,前端工程師是當(dāng)前各大企業(yè)都比較稀缺的人才,薪資待遇和就業(yè)前景都很不錯。作用域鏈的前端,始終是當(dāng)前執(zhí)行代碼所在環(huán)境的變量對象。 好程序員Web前端培訓(xùn)入門之JS基礎(chǔ)知識梳理匯總,Web前端工程師是當(dāng)前各大企業(yè)都比較稀缺的人才,薪資待遇和就業(yè)前景都很不錯。不論是專業(yè)還是非專業(yè),有基礎(chǔ)亦或是無基礎(chǔ),都想通過學(xué)習(xí)Web前端實現(xiàn)高薪就業(yè)。不過,學(xué)習(xí)要一...
摘要:好程序員前端培訓(xùn)入門之基礎(chǔ)知識梳理匯總,前端工程師是當(dāng)前各大企業(yè)都比較稀缺的人才,薪資待遇和就業(yè)前景都很不錯。作用域鏈的前端,始終是當(dāng)前執(zhí)行代碼所在環(huán)境的變量對象。 好程序員Web前端培訓(xùn)入門之JS基礎(chǔ)知識梳理匯總,Web前端工程師是當(dāng)前各大企業(yè)都比較稀缺的人才,薪資待遇和就業(yè)前景都很不錯。不論是專業(yè)還是非專業(yè),有基礎(chǔ)亦或是無基礎(chǔ),都想通過學(xué)習(xí)Web前端實現(xiàn)高薪就業(yè)。不過,學(xué)習(xí)要一...
摘要:引言滿滿的干貨,面試必系列,參考大量資料,并集合自己的理解以及相關(guān)的面試題,對核心知識點中的作用域閉包上下文進(jìn)行了梳理。如果在小區(qū)這個作用域找到了張老師,我就會在張老師的輔導(dǎo)下學(xué)鋼琴我張老師房間鋼琴構(gòu)成了學(xué)琴的上下文環(huán)境。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 滿滿的干貨,面試必bei系列,參考大...
摘要:前言這段時間一直在消化作用域鏈和閉包的相關(guān)知識。而作用域鏈則是這套規(guī)則這套規(guī)則的具體運行。是變量對象的縮寫那這樣放有什么好處呢我們知道作用域鏈保證了當(dāng)前執(zhí)行環(huán)境對符合訪問權(quán)限的變量和函數(shù)的有序訪問。 前言:這段時間一直在消化作用域鏈和閉包的相關(guān)知識。之前看《JS高程》和一些技術(shù)博客,對于這些概念的論述多多少少不太清楚或者不太完整,包括一些大神的技術(shù)文章。這也給我的學(xué)習(xí)上造成了一些困惑,...
閱讀 2696·2021-10-12 10:12
閱讀 804·2019-08-29 17:25
閱讀 2816·2019-08-29 17:24
閱讀 3256·2019-08-29 17:19
閱讀 1825·2019-08-29 15:39
閱讀 3086·2019-08-26 16:50
閱讀 2013·2019-08-26 12:17
閱讀 2728·2019-08-26 12:16