摘要:在函數內部的變量稱之為局部變量,它可以在函數內部讀取,在函數外部無法正常讀取,如果想要讀取函數內部的變量則需要用到閉包。
前端面試之閉包
閉包屬于屬于JavaScript的難點,但是在很多高級應用都需要用到,也是前端面試中經常會考到的點。
作用域談到閉包首先必須了解作用域,ES5中,JavaScript的作用域只有兩種,一種是全局作用域,變量在整個程序中一直存在,所有地方都可以讀取;另一種是函數作用域,變量只在函數內部存在。
JavaScript中變量分為兩種:全局變量,局部變量。
全局變量在程序中的任何一個位置都可以調用,及賦值。
在函數內部的變量稱之為局部變量,它可以在函數內部讀取,在函數外部無法正常讀取,如果想要讀取函數內部的變量則需要用到閉包。父函數內部定義了子函數,子函數可以引用父函數作用域中的變量。
在網上找了一個圖比較好的解釋了變量與作用域之間的微妙關系。
必須注意的一點的是函數本身的作用域,是定義時的作用域,這里與this的指向不同。
var a = 1; var f = function(){ console.log(a); } function f2(){ var a = 2; f(); } f2() // 1
函數f在全局作用域下定義的,雖然在f2中被引用,但是a仍然是全局作用域下的a。
javascript 中的垃圾收集機制在談到閉包之前,還有一個垃圾回收機制需要了解。
JavaScript的內存生命周期:
分配所需要的內存
使用分配到的內存(讀、寫)
不需要時將其釋放
垃圾回收機制的原理其實很簡單:確定變量中哪些還在繼續使用的,哪些已經不用的,然后垃圾收集器每隔固定的時間就會清理一下,釋放內存。
局部變量在程序執行過程中,會為局部變量分配相應的空間,然后在函數中使用這些變量,如果函數運行結束了,而且在函數之外沒有仔引用這個變量了,局部變量就沒有存在的價值了,因此會被垃圾回收機制回收。在這種情況下,很容易辨別,但是并非所有情況下都這么容易。比如說全局變量。在現代瀏覽器中,通常使用標記清除策略來辨別及實現垃圾回收(還有一種叫引用計數,即當變量的引用次數為零的時候,就表示不再使用,這里有個循環計數的bug,現代瀏覽器已經不再使用它)。
標記清除
標記清除會給內存中所有的變量都加上標記,然后去掉環境中的變量以及不在環境中但是被環境中變量引用的變量(閉包)的標記。剩下的被標記的就是等到被刪除的變量,原因是環境中的變量已經無法訪問到這些變量了。最后垃圾回收器會完成內存清理,銷毀那些被標記的值釋放內存空間。
閉包首先拋出一條閉包的定義:閉包是指這樣的作用域,它包含有一個函數,這個函數可以調用被這個作用域所封閉的變量、函數或者閉包等內容。
由定義可以看出:
閉包指的是一個函數作用域
閉包包含一個函數,且這個函數調用了作用域里的內容
滿足這兩點,都可以叫做閉包。
正常情況下,一個函數執行完,且沒有在任何地方被調用,這個函數將會被垃圾回收機制銷毀。
舉個例子:
var obj = function () { var a = ""; return { set: function (val) { a = val; }, get: function () { return a; } } }; var b = obj(); b.set("new val"); b.get();
obj這個函數在執行完之后理論上 函數體內的東西都應該被回收掉。但它執行后的返回值 b 具有set和get方法。這兩個方法里對a保持了引用,所以obj執行過程中產生的a就不會銷毀。直到b先被回收,這個a才會回收。
閉包利用的就是以上原理,以下是一個閉包的例子:
function f1() { var a = 1; function f2() { console.log(a); } return f2; } var a = 2; var f = f1(); f() // 1
f執行完之后,其實f指向的f2這個函數在f1這個函數的作用域的引用,也就是說,執行f,相當于在f1函數的作用域這個環境下,執行f2。原因是f2是在f1中定義的,而且在這個例子中,a的值不會受外界影響。
閉包的特點1.閉包內的變量不會影響到全局變量,也不會被全局變量所影響
2.閉包中被函數引用的局部變量不會被垃圾回收機制回收
3.可以創建私有變量和私有函數
4.可以把需要公開的變量和方法綁定在window上放出來
(function() { // 私有變量 var age = 20; var name = "Tom"; // 私有方法 function getName() { return `your name is ` + name; } // 共有方法 function getAge() { return age; } // 將引用保存在外部執行環境的變量中,形成閉包,防止該執行環境被垃圾回收 window.getAge = getAge; })();閉包在ES6中的運用
ES6引入了塊級作用域,主要是let命令以及const命令。他們的特點是不存在變量提升,不可以重復聲明,只在區塊中有效,存在暫時性死區。
借一個阮一峰老師的暫時性死區的例子:
var tmp = 123; if (true) { tmp = "abc"; // ReferenceError let tmp; }
在條件語句的區塊中,雖然tmp在賦值后再用let命令聲明,但是let命令已經生效,不歸var所管了。
首先拋磚引玉,來一個關于ES5經典的例子:
var test = function () { var arr = []; for(var i = 0; i < 5; i++){ arr.push(function () { return i*i; }) } return arr; } var test1 = test(); console.log(test1[0]()); console.log(test1[1]()); console.log(test1[2]());
這個例子就不用多講了,最后輸出的值都是25。要注意的有兩點,一個是i的變量提升,一個是i++,i++實際作用位置為當前循環內容結束,下一個循環之前。i++的意思是當前語句結束后,i加1。當我們打印i的值的時候,i的循環已經執行完了,i已經變成5了。
當我們用ES6的時候,情況就不一樣了。
var test = function () { const arr = []; for(let i = 0; i < 5; i++){ arr.push(function () { return i*i; }) } return arr; } var test1 = test(); console.log(test1[0]()); console.log(test1[1]()); console.log(test1[2]());
因為使用let,使得for循環為塊級作用域,let i=0在這個塊級作用域中,而不是在函數作用域中。每次循環都會創建一個新的塊級作用域,i值互相獨立不受影響。所以最后打印的結果是:0,1,4.
閉包實現一個計數器var counter = function(){ var count = 1; return function(){ return count++; } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83241.html
摘要:閉包面試題解由于作用域鏈機制的影響,閉包只能取得內部函數的最后一個值,這引起的一個副作用就是如果內部函數在一個循環中,那么變量的值始終為最后一個值。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...
摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數,也不是局部變量,所以是自由變量。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...
摘要:今天同學去面試,做了兩道面試題全部做錯了,發過來給道典型的面試題前端掘金在界中,開發人員的需求量一直居高不下。 排序算法 -- JavaScript 標準參考教程(alpha) - 前端 - 掘金來自《JavaScript 標準參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡介 算法實現 選擇排序 簡介 算法實現 ... 圖例詳解那道 setTimeout 與循環閉包的經典面...
摘要:最近遇到的前端面試題更新版前端掘金個人博客已上線,歡迎前去訪問評論無媛無故的個人博客以下內容非本人原創,是整理后覺得更容易理解的版本,歡迎補充。 一道面試題引發的對 javascript 類型轉換的思考 - 前端 - 掘金 最近群里有人發了下面這題:實現一個函數,運算結果可以滿足如下預期結果: ... 收集 JavaScript 各種疑難雜癥的問題集錦 - 前端 - 掘金 從原博客遷移...
摘要:最近遇到的前端面試題更新版前端掘金個人博客已上線,歡迎前去訪問評論無媛無故的個人博客以下內容非本人原創,是整理后覺得更容易理解的版本,歡迎補充。 一道面試題引發的對 javascript 類型轉換的思考 - 前端 - 掘金 最近群里有人發了下面這題:實現一個函數,運算結果可以滿足如下預期結果: ... 收集 JavaScript 各種疑難雜癥的問題集錦 - 前端 - 掘金 從原博客遷移...
閱讀 2419·2021-10-11 10:57
閱讀 1274·2021-10-09 09:59
閱讀 1986·2019-08-30 15:53
閱讀 3206·2019-08-30 15:53
閱讀 1001·2019-08-30 15:45
閱讀 727·2019-08-30 15:44
閱讀 3432·2019-08-30 14:24
閱讀 946·2019-08-30 14:21