春暖花開,又到了程序猿換領地的季節了,各大論壇出現不少的面試題和各類押題,而後我和小夥伴仔細研究,果真大部分不會。其中有一個講zepto源碼的,提到了js三座大山:閉包,原型和異步。我曾經入門的時候在這3個山裏面饒了好久好久,並且屢次覺得繞出去的時候才發現我只是過了一個小山頭,苦不堪言。固然,我以爲如今本身已經繞出來了,那麼就先講一講閉包這座山的路。javascript
先放一段經典的面試題java
for(var i = 0; i < 10; i ++) { bt[i].onclick = function(){ console.log(i); } }
初次接觸閉包就是這個問題,可是這樣看,好像和閉包沒有什麼關係,並且確實沒有什麼關係。這個每次結果都是9已經毫無疑問了,可是爲何是9?咱們慢慢拆解。面試
var i; for(i = 0; i < 10; i ++) { bt[i].onclick = function(){ console.log(i); } }
i做爲變量被提高了,咱們再把循環展開瀏覽器
bt[0].onclick = function(){ console.log(i); } bt[1].onclick = function(){ console.log(i); } bt[2].onclick = function(){ console.log(i); } ....
這時有人確定和我當年有同樣的疑問,爲何function裏的是i,不該該對應的是當時的i的值嗎?閉包
先舉個例子異步
var i = 10; function test(){ var i = 1; console.log(i); }
若是i在js加載就賦值了,那麼是否是就成了console.log(10)
,test()執行後打印的應該是10而不是1啊。很明顯,解析js的時候,test這個函數內部並無賦值,只是給它了一個變量,具體什麼值等調用了纔會拿到。其實拆到這的時候我也很懵,什麼鬼,爲何會是這樣。函數
在ES6以前是沒有塊級做用域這個概念的,只有全局做用域、函數做用域和eval做用域。this
在瀏覽器加載js腳本的時候,解析器在解析js代碼時,會先進入全局做用域,若是碰到函數調用,會創造函數上下文,並將這個上下文壓入到執行棧中,若是這個函數內部還有另外一個函數調用,那麼就會把另外一個函數壓入到執行棧的頂部,依次類推,最後調用的函數老是被放到執行棧的最頂部,而後執行最上層的函數。線程
這個過程涉及到js很重要的幾個點:js是單線程,同步執行,函數被調用的時候纔會建立執行上下文。code
既然知道了函數調用的時候會建立執行上下文,那麼就要探究下怎麼執行上下文內部怎麼執行的:
上面這些步驟能夠分爲2個階段:初始化階段(建立階段)和執行階段。
咱們看個例子
var a = 100; function fn(n) { var a = 10; const b = function(){}; function c(o) {} }
當在調用fn(11)時,建立階段實際上是這樣的:
function fn(n) { a:100 //父級上下文中的變量(做用域鏈) arguments:{ 0:11, length:1 } c:function c(o) a: undefined b: undefined this:{//全局是window} }
很明顯,在建立階段只是作了屬性名的定義,並無給函數內變量賦值,全局變量和參數除外。建立階段完成後,便開始進入執行階段。代碼執行階段看起來是這樣得:
function fn(n) { a:100 //父級上下文中的變量(做用域鏈) arguments:{ 0:11, length:1 } c:function c(o) a: 10 b: function() this:{//全局是window} }
這就是整個執行環境。
說了這麼多廢(pu)話(dian),終於到正題了。紅皮書是這樣說的:閉包是有權訪問另外一個函數做用域中的變量的函數。若是熟讀紅皮書的話應該很好理解這句話,主要在做用域鏈那。固然通俗點就是內部能夠訪問外部,外部不能訪問內部。
如今返回去看看那道經典的題目,應該很好解決了。根據執行上下文的過程,咱們在外層加一個函數,而且執行它,由於初始化過程當中參數是能夠直接被賦值的。
for(var i = 0; i < 10; i ++) { (function(num){ bt[i].onclick = function(){ console.log(num); } })(i) }
這樣就ok了。
閉包並不可怕,只要搞清js的一些執行機制,不少也就迎刃而解了。當時我並不理解爲何要加一個理解執行函數,後來看到不少關於做用域鏈和執行上下文的文章,反過來想一想,也就沒什麼難的了。固然,閉包一個很重要的做用就是設置私有變量,其實這個題目也算一個。
這個講的很簡單,若是仔細說閉包,估計要說2小時。這個只是來理解這一個題目而已。
他的原創之處並不優秀,他的優秀之處並不是原創!!!