JavaScript的this和閉包
作為OO語言,c++、c#或者Java等語言,都有this的概念,JavaScript中存在this的概念,一般編程語言的this就是對象自己,而JavaScript的this卻不一定!
在JavaScript中,this所引用的對象(很多書籍叫函數上下文,我也這樣叫吧)并不是由聲明函數的方式決定的,而是由調用函數的方式決定的,下面看代碼:
var o2 = {handle : 'o2' } ;
var o3 = {handle : 'o3' } ;
window. handle = 'window' ;
function whoAmI ( ) {
return this. handle ;
}
o1. identifyMe = whoAmI ;
alert (whoAmI ( ) ) ; // 輸出window
alert (o1. identifyMe ) ; // 輸出o1
alert (whoAmI. call (o2 ) ) ; // 輸出o2
alert (whoAmI. apply (o3 ) ) ; // 輸出o3
以上輸出說明了下面幾個問題:
- 當直接把函數作為頂層函數來調用時,函數上下文是window;
- 當把函數作為對象的屬性來調用時,該對象就成為函數調用的函數上下文;
- 使用Function的call()方法把函數上下文設置為傳入call()的第一個參數的任何對象,這時該傳入對象成為函數調用的函數上下文;
順便補充介紹下Function類的call()和apply()方法,它們都是用來調用函數的。
(1)call()方法
call(thisObject[, parameter])
參數thisObject是一個類,指定函數體內this關鍵字的值,該參數是必須的;
參數parameter是可選的,是要傳遞給函數的參數,可以指定0或多個參數(參數為用逗號分隔的列表)。
(2)apply()方法
apply(thisObject[, argArray])
與call不同的是,第二個參數是用數組模式來傳遞的。
下面是閉包的內容了(下面部分內容參考于網絡)
簡單說下什么是閉包:
- 閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在;
- 閉包就是就是函數的“堆棧”在函數返回后并不釋放,我們也可以理解為這些函數堆棧并不在棧上分配而是在堆上分配;
- 當在一個函數內定義另外一個函數就會產生閉包;
上面的第二定義是第一個補充說明,抽取第一個定義的主謂賓——閉包是函數的“局部變量”集合。只是這個局部變量是可以在函數返回后被訪問。(這個不是官方定義,但是這個定義應該更有利于你理解閉包)。
做為局部變量都可以被函數內的代碼訪問,這個和靜態語言是沒有差別。閉包的差別在于局部變變量可以在函數執行結束后仍然被函數外的代碼訪問。這意味著函數必須返回一個指向閉包的“引用”,或將這個”引用”賦值給某個外部變量,才能保證閉包中局部變量被外部代碼訪問。當然包含這個引用的實體應該是一個對象,因為在Javascript中除了基本類型剩下的就都是對象了。可惜的是,ECMAScript并沒有提供相關的成員和方法來訪問閉包中的局部變量。但是在ECMAScript中,函數對象中定義的內部函數(inner function)是可以直接訪問外部函數的局部變量,通過這種機制,我們就可以以如下的方式完成對閉包的訪問了。
請看以下代碼:
var text = 'hello ' + name ;
return function ( ) { alert (text ) ; }
}
var sayHello = hello ( 'jack' ) ;
sayHello ( ) ; // 這里通過閉包訪問到了hello()中的text參數
再看一段定時器代碼
var local = 1 ;
window. setInterval ( function ( ) {
alert (local ) ; // 這里通過閉包訪問到了local變量
local ++;
} , 1000 ) ;
} ) ;
另外,閉包有一個重要的特征:函數上下文絕不會被包含為閉包的一部分。例如下面的代碼不會按照我們期望的方式執行:
this. id = 'someID' ;
$ ( '*' ). each ( function ( ) {
alert ( this. id ) ;
} ) ;
每個函數調用都有其函數上下文(this指向的對象),因此上面的代碼,傳遞給each()的函數上下文在回調函數內是來自于jQuery包裝集的元素,而不是被設置為”someID”的外部函數屬性。對回調函數的每個調用都會依次彈出警告框,用來顯示包裝集中各個元素的id。
如果需要訪問在外部函數中作為函數上下文的對象,可以考慮通常的習慣用法:在局部變量中創建this引用的副本,這個副本將會被包含在閉包中。
this. id = "someID" ;
var _that = this ;
$ ( '*' ). each ( function ( ) {
alert (_that. id ) ;
} ) ;