Javascript的this關(guān)鍵字與scope詳解
Javascript this關(guān)鍵字一般來說大家并不陌生,但是如何更好的用好Javascript this關(guān)鍵字卻需要我們仔細(xì)思考,希望本文對(duì)廣大程序員有所幫助。
摘要
本系列博文主要談一些在 Javascript 使用中經(jīng)常會(huì)混淆的高級(jí)應(yīng)用,包括: prototype, closure, scope, this關(guān)鍵字. 對(duì)于一個(gè)需要提高自己Javascript水平的程序員,這些都是必須要掌握的.
本節(jié)主要介紹this關(guān)鍵字和scope.
本文的Javascript源代碼從 這兒 下載.
Contents
摘要引入this關(guān)鍵字關(guān)于apply和callscope詳述一些說明結(jié)論后記參考資料本文的源碼
引入
作為一個(gè)程序員, 你可能早已經(jīng)習(xí)慣于面向?qū)ο笳Z言中指代當(dāng)前對(duì)象的引用(或者指針), 如的c++中的this或者 python 中的self,當(dāng)然具有OO屬性( Javascript 其實(shí)更多的是一種所謂的函數(shù)式語言)的 Javascript 同樣, 它也具有引用當(dāng)前屬性的對(duì)象的指針(或者引用), 也就是this關(guān)鍵字.
為了理解this關(guān)鍵字,如果你只想記住一句話,那應(yīng)該是 this關(guān)鍵字總是指向當(dāng)前函數(shù)的所有者對(duì)象(執(zhí)行空間), 至于這句話如何理解, 可以參見下面的詳細(xì)說明.
那么什么是 scope 呢?
wikipedia 中的解釋是 In computer programming, scope is an enclosing context where values and expressions are associated. 中文即是所謂的 作用域, 它指明的是一個(gè)數(shù)值或者表達(dá)式所關(guān)聯(lián)的上下文(能夠被引用的執(zhí)行空間).
scope 與this有什么關(guān)系呢? 如果從上面的定義來看, this指向的總是當(dāng)前引用此函數(shù)的對(duì)象,而當(dāng)你要判斷當(dāng)前引用的對(duì)象時(shí), 這時(shí)你就得弄清楚當(dāng)前函數(shù)所在的 scope. 具體可見下面的分析.
Javascript this關(guān)鍵字
請(qǐng)看下面的幾個(gè)例子.
一個(gè) python 的例子:
- class Person(object):
- """a person class
- """
- def __init__(self, name):
- self.name = name #這里的self指向的是實(shí)例化后的對(duì)象,如下面中的zhutao
- def get_name(self):
- return self.name
- zhutao = Person("zhutao")
- print zhutao.name
- 一個(gè) Javascript 的例子:
- window.name = "zhutao from window"
- var get_name = function(){
- return this.name; // this的具體指向只能在運(yùn)行時(shí)才能確定,也就是確定運(yùn)行時(shí)調(diào)用其的對(duì)象
- };
- alert(get_name()); // 輸出zhutao from window, get_name調(diào)用的對(duì)象為window
- var obj = {}
- obj.name = "zhutao from obj";
- alert(get_name.apply(obj)); // 輸出zhutao from obj, 我們強(qiáng)制地使用了 apply來更改調(diào)用的對(duì)象,使其指向obj
- var innerobj = {
- "name" : "zhutao from innerobj"
- };
- innerobj.get_name = get_name; // 使得innerobj的get_name方法指向了global scope的get_name函數(shù)
- alert(innerobj.get_name()); // 輸出zhutao from innerobj, 此時(shí)this指向的是innerobj
那么從上面的簡(jiǎn)單例子來看, this 總是在 運(yùn)行時(shí) 才能確定其具體的指向, 也才能知道它的調(diào)用對(duì)象.而 這點(diǎn)也正是 動(dòng)態(tài)語言 一個(gè)重要特性.
那么如何確定當(dāng)前this指向的引用對(duì)象呢? 通??梢赃@樣判斷:
如果在global的scope(可以參見下面的說明來明確什么是global scope)來調(diào)用,則指向的是bowser的***對(duì)象window 例如: get_name()
如果, 有類似于這樣的引用, innerobj.get_name() 則很顯然this指向的是innerobj
如果我們使用了apply, call來進(jìn)行強(qiáng)制的引用對(duì)象指向, 則也會(huì)很顯然地指向強(qiáng)制的對(duì)象,如 get_name.apply(obj).
關(guān)于apply和call
這2個(gè)關(guān)鍵字可以很簡(jiǎn)單地理解為 進(jìn)行this引用對(duì)象(運(yùn)行空間)強(qiáng)制轉(zhuǎn)換, 二者的語法如下:
- fun.call(object, arg1, arg2, ...)
- fun.apply(object, [arg1, arg2, ...])
二者目的是相同的(動(dòng)態(tài)更改函數(shù)的運(yùn)行空間, 或者稱作更改this指向的對(duì)象), 只是在提供給函數(shù)的參數(shù)上的調(diào)用方法不同.
示例代碼如下:
- var test_call_apply = function(name, gender, school){
- alert(this.age + name + gender + school);
- };
- test_call_apply.call({age:24}, "zhutao", "male", "ISCAS");
- test_call_apply.apply({age:24}, ["zhutao", "male", "ISCAS"]);
scope詳述
先看下面幾個(gè)例子:
- var global_scope = "I'm global";
- var fun = function(){
- var fun_scope = "I'm in fun scope";
- return innerfun(){
- var inner_func_scope = "I'm in the inner fun scope";
- return global_scope + fun_scope + inner_func_scope; //此處的引用是重要的,請(qǐng)?zhí)貏e注意
- };
- };
- alert(fun()());
請(qǐng)注意上面的代碼,其中:
global_scope 它是global scope
fun_scope 它是 位于一個(gè)函數(shù)的scope
inner_func_scope 是一個(gè)位于一個(gè)函數(shù)內(nèi)的函數(shù)的scope
你也可以繼續(xù)內(nèi)嵌函數(shù), 那么會(huì)生成若干個(gè)scope.
于是有個(gè)問題出現(xiàn)了, 為什么innerfun方法可以引用不在它自身scope的變量?
在回答這個(gè)問題之前,需要引入一個(gè)概念 scope chain. 所謂的 scope chain 是指 在 Javascript 的代碼中形成的一個(gè)具有優(yōu)先順序, 相關(guān)的作用域的鏈.
以上面的代碼為例,
對(duì)于global的scope而言,它會(huì)為自己建立一個(gè)global的scope chain(當(dāng)然此時(shí),這個(gè)鏈只有一個(gè)scope).對(duì)于fun函數(shù)的scope而言, 它首先建立一個(gè)與global相同的scope chain,然后再加入自己的scope(此時(shí),這個(gè) 鏈有2個(gè)scope), 類似于這樣的結(jié)構(gòu): global==>fun對(duì)于innerfun而言,除了fun函數(shù)所具有的鏈外,它還會(huì)加入自己的scope(當(dāng)然,此時(shí)這個(gè)鏈有3個(gè)scope), 類似于這樣的結(jié)構(gòu): global==>fun==>innerfunscope chain具有下面的特征:
有序每當(dāng)建立一個(gè)函數(shù)時(shí),會(huì)自動(dòng)生成一個(gè)scope并加入自己的scope chain中這個(gè)chain類似于一種棧,在查找變量時(shí)總是先從頂端查起參見下圖:
上圖的3個(gè)部分對(duì)應(yīng)上面代碼中的三個(gè)變量的scope, 并且在對(duì)每個(gè)變量求值時(shí),是按照 圖中的scope chain從上到下依次查找,找到即返回值或者直到窮舉了scope chain返回undfined.
那么現(xiàn)在回答上面那個(gè)問題:
其實(shí)也很好理解, 在計(jì)算某個(gè)表達(dá)式時(shí), 它會(huì)對(duì)自己的scope chain進(jìn)行從上到下的查找,如果找到了 它會(huì)立即返回這個(gè)值,如果找完了整個(gè)chain也沒有找到,則返回undefined.
這個(gè)查找機(jī)制也就決定了,通常位于chain的前端的scope有更高的優(yōu)先級(jí).
例如 Javascript 在計(jì)算 global_scope + fun_scope + inner_func_scope; 這個(gè)表達(dá)式時(shí), 它會(huì)查找上面圖示中的scope chain,從而確定出***的結(jié)果.
一些說明
如果你弄清楚了上面的論述, 應(yīng)該說你對(duì)this關(guān)鍵字和scope已經(jīng)具有完全的知識(shí)基礎(chǔ)了,但是 我們需要在實(shí)際中更好地使用和理解這些概念,這樣才能把能力上升到別一個(gè)層次, 這也即所謂的 理論與實(shí)踐 的關(guān)系.
請(qǐng)看下面這個(gè)例子:
- var change_color = function(){
- this.style.color = "red";
- };
- window.onload = function(){
- var text = document.getElementById("text");
- text.onclick = change_color; //此時(shí)this指向的是text這個(gè)對(duì)象(dom對(duì)象)
- };
- // 下面這行代碼是在body中
- <span id="another" onclick="change_color()">My color will be changed2.</span> //這點(diǎn)需要特別注意, inline script指向的是window,此處會(huì)無定義
需要特別注意的是:
inline event registration中的this并非指向的是自己的dom節(jié)點(diǎn),而是global scope的window,這點(diǎn)可以從上面的例子中得到證明這種inline event registration是不可取的, 推薦的是 Unobtrusive Javascript (處理邏輯和頁(yè)面結(jié)構(gòu)相分離)結(jié)論
Javascript 是一種非常強(qiáng)大的動(dòng)態(tài)語言, 它是 披著C語言外衣的函數(shù)式語言, 如果你只當(dāng)作它是一種 類C的命令式語言,那么你的知識(shí)層次還過于低, 而倘若你能夠理解到Javascript 的函數(shù)式語言本質(zhì), 你在運(yùn)用 Javascript ,理解 jQuery 及其它的庫(kù), 甚至自己寫一些 Javascript 都會(huì)游刃有余的.
后記
本系列的計(jì)劃的內(nèi)容已經(jīng)結(jié)束,除了這些而外, 我還想寫一至二篇補(bǔ)遺的 Javascript 的高級(jí)知識(shí)來作為本系列的終結(jié). 可能會(huì)寫的內(nèi)容包括:
Javascript 函數(shù)式語言特征探究Javascript 相關(guān)庫(kù)的分析Unobtrusive Javascript 的一些理解和實(shí)踐總之, Javascript 本身是很值得探究的一個(gè)語言, 也有很多的值得一書的地方, 我希望后續(xù)能夠不斷地完成這個(gè)計(jì)劃.
其實(shí),之前寫過 Django開發(fā)必知必會(huì) 以及本系列的 Javascript必知必會(huì), 發(fā)現(xiàn)在寫這些內(nèi)容的同時(shí),自己的相關(guān) 知識(shí)也有了很大的提高, 寫的同時(shí), 站的角度不只是作為一個(gè) 自學(xué)者 ,而是作為一個(gè) 教者 ,我希望能夠看到這些 內(nèi)容的讀者也能夠受益. 所以我想后續(xù),我可能會(huì)寫一些 計(jì)算機(jī)科學(xué) 的其它專題, 如:
python必知必會(huì)正則表達(dá)式必知必會(huì)Web開發(fā)必知必會(huì)等等如果這個(gè)"宏偉"的計(jì)劃得以完成, 我想也就成就我自已定義的一個(gè) 優(yōu)秀程序員 的知識(shí)基礎(chǔ).
這是一個(gè)初步的計(jì)劃, 我會(huì)逐漸展開的. 希望大家能夠不斷反饋.
原文標(biāo)題:javascript必知必會(huì)之this關(guān)鍵字及scope
鏈接:http://www.cnblogs.com/mindsbook/archive/2009/09/27/javascriptYouMustKnow-this-scope.html
【編輯推薦】