云天视角-浅谈闭包
一、現狀
閉包是jser繞不過的坎,一直在都在說,套用 simpson 的話來說:JavaScript中閉包無處不在,你只需要能夠識別并擁抱它。
閉包是基于詞法作用域書寫代碼時的自然結果,你甚至不需要為了利用它們而有意識的去創建閉包。閉包的創建和使用在你的代碼中隨處可見。你缺少的只是根據你的意愿來識別、擁抱和影響閉包的思維環境
二、什么是閉包(closure)
當函數可以記住并訪問所在的詞法作用域時,就產生了閉包。即使函數是在當前詞法作用域之外執行 --《你不知道的js》(上卷) 閉包是指有權訪問另一個函數作用域中的變量的函數 --《JavaScript高級程序設計》先來看一個例子:
例子1:
這是閉包嗎?
這個代碼從技術上來說是,但也可以說不是。準確的來說bar()對a的引用的方法是詞法作用域的查找規則。我們再來看:
例子2:
在例2中,我們將bar()函數本身當做一個值類型進行傳遞,函數bar()能夠訪問foo()的內部作用域。在這個例子中,它在自己定義的詞法作用域以外的地方執行。
三、怎么形成的
要了解清楚,得先了解幾個概念
- 作用域鏈(scope chain)
- 詞法作用域
詞法作用域
每個函數都有自己的執行環境。這個環境可以訪問外部環境,以此類推。每個環境能訪問到的標識符集合,稱之為 作用域,也就是詞法作用域。
作用域鏈(scope chain)
將作用域一層一層的嵌套,就形成了作用域鏈
如下,通常我們都希望foo()在執行完成以后,整個的內部作用域都被銷毀。因為我們知道引擎有垃圾回收機制用來釋放不再使用的內存空間。由于看上去foo()的內容不會再被使用,所以很自然的想到會對其回收。但是,事實上內部作用域依然存在
var globalVar = 10; function foo() {var fooVar = 20;function bar() {var barVar = 30;return globalVar + fooVar + barVar;}return bar; } var baz = foo(); baz();如上,用一張圖表示
這個作用域鏈在函數創建的時候就保存起來了。
baz()函數在執行的時候(執行bar()函數),將當前的變量對象(由于當前的環境是函數,所以將其活動對象作為變量對象)添加到作用域鏈的前端。此時,由于bar()在執行,而作用域鏈也存在,所以可以在作用域鏈上進行查找,去訪問foo()的變量。
四、閉包的應用場景有哪些
- 創建私有變量或函數
五、閉包的缺點
- 閉包中的值是存在于內存中,濫用的話會導致內存消耗過大
閉包經典問題
// 函數作用:希望它返回一個數組。該數組的元素為遍歷的索引值 function hello(){var res = [];for (var i = 0,len = 5;i < len;i++){res[i] = function () {return i;}}return res; }返回的結果跟我們期待的不一樣,因為:閉包保存的是整個變量對象,而不是每個變量。
解決方案:
這里,沒有沒有把閉包直接賦值給數組。而是定義了一個匿名函數,并且將立即執行該匿名函數的結果賦值給數組,由于參數是按值傳遞的,所以會將當前值傳給參數num。
參考資料:《你不知道的js》(中卷)、《JavaScript高級程序設計》完
總結
- 上一篇: 13.首页内容展示
- 下一篇: Struts2第十一篇【简单UI标签、数