在 JavaScript 中,函數(shù)通常運(yùn)行在一個(gè)特定的上下文中(通常稱為“作用域[scope]”)。在函數(shù)內(nèi)部, this 關(guān)鍵字是指向那個(gè)作用域的引用。實(shí)際上,每個(gè)函數(shù)都是對(duì)象的屬性——全局函數(shù)是 window 對(duì)象的屬性——運(yùn)行時(shí)函數(shù)的作用域即為函數(shù)被調(diào)用時(shí)函數(shù)所屬的對(duì)象, 更嚴(yán)格的說法是保存了對(duì)函數(shù)的引用的對(duì)象。
javascript是一門動(dòng)態(tài)語言,最明顯就是那個(gè)dynamic this。它一般都是作為函數(shù)調(diào)用者存在。在javascript,所有關(guān)系都可以作為對(duì)象的一個(gè)關(guān)聯(lián)數(shù)組元素而存在。那么函數(shù)就是被掰成兩部分儲(chǔ)存于對(duì)象,一是其函數(shù)名(鍵),一是函數(shù)體(值),那么函數(shù)中的this一般都指向函數(shù)所在的對(duì)象。但這是一般而已,在全局調(diào)用函數(shù)時(shí),我們并沒有看到調(diào)用者,或者這時(shí)就是window。不過,函數(shù)聲明后,其實(shí)并沒有綁定到任何對(duì)象,因此我們可以用call apply這些方法設(shè)置調(diào)用者。
一個(gè)簡(jiǎn)單的例子:
這里你不能說run是作為window的一個(gè)屬性而存在,但它的確是被window屬性調(diào)用了。實(shí)質(zhì)上大多數(shù)暴露在最上層的東西都則window接管了。在它們需要調(diào)用時(shí)被拷貝到window這個(gè)對(duì)象上(不過在IE中window并不繼承對(duì)象),于是有了window['xxx']與 window.xxx性能上的差異。這是內(nèi)部實(shí)現(xiàn),不深究了。
另一個(gè)例子,綁定到一個(gè)明確的對(duì)象上
答案顯然易見,this總是為它的調(diào)用者。但如果復(fù)雜一點(diǎn)呢?
盡管它是定義在object內(nèi)部,盡管它是定義run函數(shù)內(nèi)部,但它彈出的既不是object也不是run,因?yàn)樗炔皇莖bject的屬性也不是run 的屬性。它松散在存在于run的作用域用,不能被前兩者調(diào)用,就只有被window拯救。window等原生對(duì)象浸透于在所有腳本的內(nèi)部,無孔不入,只要哪里需要到它做貢獻(xiàn)的地方,它都義不容辭。但通常我們不需要它來幫倒忙,這就需要奠出call與apply兩大利器了。
call與apply的區(qū)別在于第一個(gè)參數(shù)以后的參數(shù)的形式,call是一個(gè)個(gè),aplly則都放到一個(gè)數(shù)組上,在參數(shù)不明確的情況,我們可以借助 arguments與Array.slice輕松搞定。
由此Prototype開發(fā)者搞了一個(gè)非常有名的函數(shù)出來,bind!以下是它的一個(gè)最簡(jiǎn)單的版本:
3 |
return fn.apply(context, arguments); |
不過為了面對(duì)更復(fù)雜的情況建議用以下版本。
1 |
function bind(context,fn) { |
2 |
var args = Array.prototype.slice.call(arguments, 2); |
3 |
return args.length == 0 ? function() { |
4 |
return fn.apply(context, arguments); |
它還有一個(gè)孿生兄弟叫bindAsEventListener ,綁定事件對(duì)象,沒什么好說的。
1 |
var bindAsEventListener = function(context, fn) { |
3 |
return fn.call(context, (e|| window.event)); |
1 |
Function.prototype.bind = function() { |
2 |
if (arguments.length < 2 && (typeof arguments[0]==='undefined')) |
4 |
var _slice = Array.prototype.slice |
5 |
var __method = this, args = _slice.call(arguments,0), context = args.shift(); |
7 |
return __method.apply(context, args.concat(_slice.call(arguments,0))); |
有綁定就有反綁定,或者叫剝離更好!例如原生對(duì)象的泛化方法我們是無法通過遍歷取出它們的
2 |
alert(i + " : "+ Array[i]) |
4 |
for(var i in Array.prototype){ |
5 |
alert(i + " : "+ Array.prototype[i]) |
要取出它們就需要這個(gè)東西:
1 |
var _slice = Array.prototype.slice; |
2 |
function unbind(fn) {//第一步取得泛化方法 |
3 |
return function(context) {//第二部用對(duì)應(yīng)原生對(duì)象去重新調(diào)用! |
4 |
return fn.apply(context, _slice.call(arguments, 1)); |
總結(jié):
this 的值取決于 function 被調(diào)用的方式,一共有四種,
* 如果一個(gè) function 是一個(gè)對(duì)象的屬性,該 funtion 被調(diào)用的時(shí)候,this 的值是這個(gè)對(duì)象。如果 function 調(diào)用的表達(dá)式包含句點(diǎn)(.)或是 [],this 的值是句點(diǎn)(.)或是 [] 之前的對(duì)象。如myObj.func 和myObj["func"] 中,func 被調(diào)用時(shí)的 this 是myObj。
* 如果一個(gè) function 不是作為一個(gè)對(duì)象的屬性,那么該 function 被調(diào)用的時(shí)候,this 的值是全局對(duì)象。當(dāng)一個(gè) function 中包含內(nèi)部 function 的時(shí)候,如果不理解 this 的正確含義,很容易造成錯(cuò)誤。這是由于內(nèi)部 function 的 this 值與它外部的 function 的 this 值是不一樣的。解決辦法是將外部 function 的 this 值保存在一個(gè)變量中,在內(nèi)部 function 中使用它來查找變量。
* 如果在一個(gè) function 之前使用 new 的話,會(huì)創(chuàng)建一個(gè)新的對(duì)象,該 funtion 也會(huì)被調(diào)用,而 this 的值是新創(chuàng)建的那個(gè)對(duì)象。如function User(name) {this.name = name}; var user1 = new User(”Alex”); 中,通過調(diào)用new User(”Alex”) ,會(huì)創(chuàng)建一個(gè)新的對(duì)象,以u(píng)ser1 來引用,User 這個(gè) function 也會(huì)被調(diào)用,會(huì)在user1 這個(gè)對(duì)象中設(shè)置名為name 的屬性,其值是Alex 。
* 可以通過 function 的 apply 和 call 方法來指定它被調(diào)用的時(shí)候的 this 的值。 apply 和 call 的第一個(gè)參數(shù)都是要指定的 this 的值。由于它們存在,我們得以創(chuàng)建各種有用的函數(shù)。
該文章在 2010/8/13 23:16:49 編輯過