前一段時間學習了一下阮一峯大佬寫的python教程,感受本身就是井底之蛙,函數式編程☹、返回函數☹、裝飾器☹、偏函數☹,這些都是些什麼東西?不過,在看過這些以後,有一種感受就是,部分知識和javascript中的閉包很是類似(也能夠說思想如出一轍),經過python與JS,認認真真的去理解一下Javascript中的閉包。javascript
我的理解:函數 A 返回函數 B、函數 B 引用了函數 A 的變量。java
百度百科:閉包就是可以讀取其餘函數內部變量的函數
。例如在javascript中,只有函數內部的子函數才能讀取局部變量,因此閉包能夠理解成「定義在一個函數內部的函數「。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。python
示例代碼:面試
function makeFunc() {
var name = "Mozilla";
function displayName() {
console.log(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc(); // Mozilla
複製代碼
閉包最經常使用於實現對象私有數據,將模塊的公有屬性、方法暴露出來。下面一段代碼'return'關鍵字將對象導出賦值給myModule,從而應用到閉包。編程
var myModule = (function (window, undefined) {
let name = "echo";
function getName() {
return name;
}
return {
name,
getName
}
})(window);
console.log( myModule.name ); // echo
console.log( myModule.getName() ); // echo
複製代碼
上次面試現時,遇到了定時器這道面試題。題目以下:瀏覽器
// 問這段代碼輸出什麼?
for( var i = 0; i < 10; i++ ) {
setTimeout(() => {
console.log(i);
}, 0);
}
// 答案是輸出 10個 10
複製代碼
考察點:
var變量提高,以及setTimeout。執行到setTimeout會往瀏覽器的定時器線程推個任務,而後當達到時間了會經過輪詢線程將回調推到宏任務隊列中,若是那個時候主線程不忙就會去宏任務隊列執行這個回調。因此定時器同步執行,回調是異步!閉包
詳細請參考 javascript的宏任務和微任務app
迴歸正軌,咱們來說閉包: 上面代碼如何作到輸出0,1,2,3,4,5, 6,7,8,9,10呢?(固然可使用let,但咱們經過閉包來實現一下)異步
for( var i = 0; i < 10; i++ ) {
((j) => {
setTimeout(() => {
console.log(j);
}, 0);
})(i)
}
複製代碼
"setTimeout"方法裏應用了閉包,使其內部可以記住每次循環所在的詞法做用域和做用域鏈。函數式編程
var oDiv = document.querySeletor("#div");
oDiv.onclick = function() {
console.log( oDiv.id );
}
複製代碼
在函數式編程中,閉包常常被用於偏函數應用和柯里化。
偏函數應用: 是傳給某個函數其中一部分參數,而後返回一個新的函數,該函數等待接收後續參數的過程。英文是partial application,也能夠譯做「局部應用」、「部分應用」、「偏應用」。
話句話講偏函數應用是一個函數,它接受另外一個函數爲參數,這個做爲參數的函數自己接收多個參數,它返回一個函數,這個函數與它的參數相比接收更少的參數。偏函數應用提早給出一部分參數,而返回的函數則會等待調用時傳入剩餘的參數。
// 給一段代碼理解理解
// Relatively flexible, more specific function generator.
function bindFirstArg(fn, a) {
return function(b) {
return fn(a, b);
};
}
// More general functions.
function add(a, b) {
return a + b;
}
add(1, 2); // 3
function multiply(a, b) {
return a * b;
}
multiply(10, 2); // 20
// More specific functions.
var addOne = bindFirstArg(add, 1);
addOne(2); // 3
addOne(3); // 4
addOne(10); // 11
addOne(9000); // 9001
var multiplyByTen = bindFirstArg(multiply, 10);
multiplyByTen(2); // 20
multiplyByTen(3); // 30
multiplyByTen(10); // 100
multiplyByTen(9000); // 90000
複製代碼
上面這段代碼,亮點不在於它能夠將某個參數綁定到任意的function,而且這個參數做爲綁定的function的第一個參數,它還能將方法綁定它本身身上做爲第一個參數,所以建立了一個可綁定的function。
python中偏函數案例:
# functools.partial就是幫助咱們建立一個偏函數的,不須要咱們本身定義int2(),
# 能夠直接使用下面的代碼建立一個新的函數int2
import functools
int2 = functools.partial(int, base=2)
int2('1000000') # 64
int2('1010101') # 85
複製代碼
簡單總結functools.partial的做用就是,把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。
是否是和上面JS代碼思想很相似?
閉包是JS中又一必須掌握的知識點,寫到最後,想到了之前回答閉包知識點的問題時,本身回答的也不是很好,記住一句函數 A 返回函數 B、函數 B 引用了函數 A 的變量
,在想一想應用場景,就能大體掌握閉包知識點了,順帶提一句,若是學過python,裝飾器這一章節就是很典型的閉包,這也是學Python要跨過去的一道門。
來看看python裝飾器代碼:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print '2013-12-25'
now()
# 輸出
# execute now():
# 2013-12-25
# 首先執行log('execute'),返回的是decorator函數,再調用返回的函數,參數是now函數,返回值最終是wrapper函數。
複製代碼