天天看點

通過一個簡單例子了解JavaScript閉包和this對象

在JavaScript裡,隻要有函數,就有閉包。可以說,閉包無處不在。但是,如果提問,“閉包到底是什麼”?大多數時候,可能感覺明明心裡清楚但苦于說不清楚,“隻可意會不可言傳”了。

同樣,

this

也是一個很抽象的概念,它往往和閉包一起出現。

我們通過一個例子,并做一些後續的改造,來說說,到底,閉包是什麼?this又是如何指定的?

1. 閉包

var number = 1;
function abc() {
    var number = 2;
    function foo() {
        console.log(number);
    }
    return foo;
}
abc()();//輸入是什麼?           

複制

答案是:2.

分析之前,我們需要知道一件事情,函數運作前後,發生了什麼?

  1. 函數運作時:函數内部的變量都會入棧。

    比如abc函數,它被執行時,變量number(=2)入棧。在函數運作期間,number是可以被函數内部的其他方法或者變量通路。

  2. 函數運作結束:棧内所有變量被銷毀。

    一般情況,函數内的變量晚些會被垃圾回收。也就是說,number(=2)這個變量将不存在,永遠無法通路。

那麼,如果,函數内出現閉包了呢?

當運作abc函數時,傳回值為一個函數,這個函數,就是一個閉包函數。

閉包,指的是一種特殊函數,這種函數會在被調用時保持當時的變量名查找的執行環境

(注:出自《JavaScript程式設計全解 [(日)》一書)。           

複制

現在可以回答文章開頭的問題了:

  1. 閉包是什麼?答:一種特殊函數。
  2. 閉包特點是什麼?答:被調用時,保留其定義時候的作用域執行環境。

回頭看例子,

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對象并不神秘,不是嗎?