在JavaScript裡,隻要有函數,就有閉包。可以說,閉包無處不在。但是,如果提問,“閉包到底是什麼”?大多數時候,可能感覺明明心裡清楚但苦于說不清楚,“隻可意會不可言傳”了。
同樣,
this
也是一個很抽象的概念,它往往和閉包一起出現。
我們通過一個例子,并做一些後續的改造,來說說,到底,閉包是什麼?this又是如何指定的?
1. 閉包
var number = 1;
function abc() {
var number = 2;
function foo() {
console.log(number);
}
return foo;
}
abc()();//輸入是什麼?
複制
答案是:2.
分析之前,我們需要知道一件事情,函數運作前後,發生了什麼?
-
函數運作時:函數内部的變量都會入棧。
比如abc函數,它被執行時,變量number(=2)入棧。在函數運作期間,number是可以被函數内部的其他方法或者變量通路。
-
函數運作結束:棧内所有變量被銷毀。
一般情況,函數内的變量晚些會被垃圾回收。也就是說,number(=2)這個變量将不存在,永遠無法通路。
那麼,如果,函數内出現閉包了呢?
當運作abc函數時,傳回值為一個函數,這個函數,就是一個閉包函數。
閉包,指的是一種特殊函數,這種函數會在被調用時保持當時的變量名查找的執行環境
(注:出自《JavaScript程式設計全解 [(日)》一書)。
複制
現在可以回答文章開頭的問題了:
- 閉包是什麼?答:一種特殊函數。
- 閉包特點是什麼?答:被調用時,保留其定義時候的作用域執行環境。
回頭看例子,
abc()
執行完之後,傳回
foo
函數(一個閉包函數)。這時,
abc
函數本應該被銷毀(包括其内部變量),但是由于閉包函數定義在
abc
内部并且通路到該函數的變量
number(=2)
,那麼,
abc
無法被銷戶,其執行環境依舊存在。
運作
foo
,按照作用域鍊查找原則,會先通路
var number = 2;
,自然,最後列印值為2.
綜上所述,閉包的作用域環境是函數定義時就決定好的,與運作無關。
現在,我們把上面的題目稍微改動一下:
2. this對象
var number = 1;
function abc() {
var number = 2;
function foo() {
console.log(this.number); // 這裡有變化!
}
return foo;
}
abc()();//輸入是什麼?
複制
答案是:1.
有了
this
攪合,這就不能從閉包的角度分析啦,那麼,
this
是什麼?
this是在運作時進行綁定的對象。
this的上下文取決于函數調用時的各種條件。
this的綁定和函數聲明的位置沒有任何關系,隻取決于函數的調用方式。
複制
簡單來說,誰調用了函數,那麼,函數内部的
this
就是這個調用者(注意:如果用
new
關鍵字調用函數,那麼,
this
預設指向這個新對象)。
回到題目,
abc()
執行完之後,傳回
foo
函數并運作,相當于:
foo(); // 相當于window.foo()
複制
那麼,這個時候,
foo
函數的調用者是
window
,即
this.number === window.number
,自然,輸出為1.
其實,
this
如何綁定,還有其他的方法,比如
call, apply, bind
,這裡就不展開講了。
小結:
當檢視一個函數時,要注意,它是如何通路變量的?!
變量名稱前面有沒有
this
至關重要,如果沒有
this
,那麼,多考慮閉包作用域;如果有
this
,多考慮調用關系。
可見,閉包和this對象并不神秘,不是嗎?