深入淺出JavaScript之閉包(Closure)
閉包(closure)是掌握Javascript從人門到深入一個非常重要的門檻,它是Javascript語言的一個難點,也是它的特色,很多高階應用都要依靠閉包實現。下面寫下我的學習筆記~
系列目錄
- 深入淺出JavaScript之閉包(Closure)
- 深入淺出JavaScript之this
- 深入淺出JavaScript之原型鏈和繼承
閉包-無處不在
在前端程式設計中,使用閉包是非常常見的,我們經常有意無意,直接或間接用到了閉包。閉包可以使傳遞資料更加靈活(比如處理一些點選事件)
!function() { var localData = "localData here"; document.addEventListener('click', //處理點選事件時用到了外部區域性變數,比如這裡的localData function(){ console.log(localData); }); }();
又比如下面這個例子:(是不是很親切~~)
!function() { var localData = "localData here"; var url = "http://www.baidu.com/"; $.ajax({ url : url, success : function() { // do sth... console.log(localData); } }); }();
再來看一個例子~~這種情況就是我們通常所說的閉包
function outer() { var localVal = 30; return function(){ return localVal; } } var func = outer(); func(); // 30
這個例子中呼叫outer()返回匿名函式function(),這個匿名函式中可以訪問outer()的區域性變數localVal,在outer()呼叫結束後,再次呼叫func()的時候,仍然能訪問到outer()的區域性變數localVal
閉包的概念
閉包,不同於一般的函式,它允許一個函式在立即詞法作用域外呼叫時,仍可訪問非本地變數。 –維基百科
閉包就是能夠讀取其他函式內部變數的函式。 –阮一峰
由於在Javascript語言中,只有函式內部的子函式才能讀取區域性變數,因此可以把閉包簡單理解成”定義在一個函式內部的函式”。
所以,在本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑
閉包的用途
這部分轉自這篇博文
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
在這段程式碼中,result實際上就是閉包f2函式。它一共執行了兩次,第一次的值是999,第二次的值是1000。這證明了,函式f1中的區域性變數n一直儲存在記憶體中,並沒有在f1呼叫後被自動清除。
為什麼會這樣呢?原因就在於f1是f2的父函式,而f2被賦給了一個全域性變數,這導致f2始終在記憶體中,而f2的存在依賴於f1,因此f1也始終在記憶體中,不會在呼叫結束後,被垃圾回收機制(garbage collection)回收。
這段程式碼中另一個值得注意的地方,就是”nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全域性變數,而不是區域性變數。其次,nAdd的值是一個匿名函式(anonymous function),而這個匿名函式本身也是一個閉包,所以nAdd相當於是一個setter,可以在函式外部對函式內部的區域性變數進行操作。
閉包-封裝
(function() { var _userId = 23492; var _typeId = 'item'; var export = {}; function converter(userId) { return +userId; } export.getUserId = function() { return converter(_userId); } export.getTypeId = function() { return _typeId; } window.export = export; //通過此方式輸出 }()); export.getUserId(); // 23492 export.getTypeId(); // item export._userId; // undefined export._typeId; // undefined export.converter; // undefined
利用閉包的特效能讓我們封裝一些複雜的函式邏輯,在這個例子中呼叫export上的方法(getUserId,getTypeId)間接訪問函式裡私有變數,但是直接呼叫export._userId是沒法拿到_userId的。這也是Node裡面常用到特性吧~
常見錯誤之迴圈閉包
下面這個案例,我們新增3個div,值分別為aaa,bbb,ccc,我們想實現的是點選aaa輸出1,點選bbb輸出2,點選ccc輸出3
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div><div id=div3>ccc</div>"; for (var i = 1; i < 4; i++) { document.getElementById('div' + i). addEventListener('click', function() { alert(i); // all are 4! }); }
結果點選aaa,bbb還是ccc都是alert(4)~~
產生這樣的問題在於這個i的值在初始化完成的時候就已經是4了
要達到我們想要的點選aaa輸出1,點選bbb輸出2,點選ccc輸出3,要用到閉包的技巧,在每次迴圈的時候,用立即執行的匿名函式把它包裝起來,這樣子做的話,每次alert(i)的值就取自閉包環境中的i,這個i來自每次迴圈的賦值i就能輸出1,2,3了
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div>" + "<div id=div3>ccc</div>"; for (var i = 1; i < 4; i++) { !function(i){ //②再用這個引數i,到getElementById()中引用 document.getElementById('div' + i). addEventListener('click', function() { alert(i); // 1,2,3 }); }(i); //①把遍歷的1,2,3的值傳到匿名函式裡面 }
思考題
如果你能理解下面兩段程式碼的執行結果,應該就算理解閉包的執行機制了。(來自阮老師)這題目總結得真秒~~
程式碼片段一
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
程式碼片段二
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());
相關文章
- 深入淺出Javascript閉包JavaScript
- Javascript閉包(Closure)JavaScript
- 學習Javascript閉包(Closure)JavaScript
- 閉包捕捉(closure capture)淺析APT
- JavaScript深入之閉包JavaScript
- JavaScript 深入之閉包JavaScript
- javascript中的閉包closure詳解JavaScript
- 深入淺出JavaScript之thisJavaScript
- JS閉包ClosureJS
- PHP 閉包(Closure)PHP
- 閉包 | 淺談JavaScript閉包問題JavaScript
- JS中的 閉包(Closure)JS
- JavaScript之閉包JavaScript
- JS-閉包(closure)的理解JS
- 深入理解javascript原型和閉包(15)——閉包JavaScript原型
- 深入淺出JavaScript之原型鏈&繼承JavaScript原型繼承
- 淺析Javascript閉包的特性JavaScript
- python closure閉包 lambda表示式Python
- 深入淺出JavaScript之原型鏈和繼承JavaScript原型繼承
- Javascript深入之作用域與閉包JavaScript
- Java中的閉包(Closure)和回撥Java
- 深入淺出 JavaScript 中的 thisJavaScript
- Python深入分享之閉包Python
- javascript之溫習閉包JavaScript
- 深入理解javascript系列(七):閉包(1)JavaScript
- 深入理解javascript系列(八):閉包(2)JavaScript
- Javascript閉包深入解析及實現方法JavaScript
- 深入理解javascript原型和閉包系列JavaScript原型
- 深入理解javascript原型和閉包(10)——thisJavaScript原型
- JavaScript變數作用域(Variable Scope)和閉包(closure)的基礎知識JavaScript變數
- [正兒八經PHP]PHP閉包(Closure)初探PHP
- 深入學習js之——閉包#8JS
- 深入理解javascript系列(九):應用閉包JavaScript
- 深入理解javascript原型和閉包(17)——補thisJavaScript原型
- 深入理解javascript原型和閉包(完結)JavaScript原型
- Javascript ClosureJavaScript
- 深入淺出 FlatBuffers 之 Schema
- 深入淺出JavaScript執行機制JavaScript