javascript里的this指針邏輯上的概念也是實(shí)例化對(duì)象,這一點(diǎn)和java語(yǔ)言里的this指針是一致的耳舅,但是javascript里的this指針卻比java里的this難以理解的多染坯,究其根本原因總結(jié)起來(lái)有三個(gè)原因:
** 原因一:javascript是一個(gè)函數(shù)編程語(yǔ)言均芽,怪就怪在它也有this指針,說(shuō)明這個(gè)函數(shù)編程語(yǔ)言也是面向?qū)ο蟮恼Z(yǔ)言单鹿,說(shuō)的具體點(diǎn)掀宋,javascript里的函數(shù)是一個(gè)高階函數(shù),編程語(yǔ)言里的高階函數(shù)是可以作為對(duì)象傳遞的,同時(shí)javascript里的函數(shù)還有可以作為構(gòu)造函數(shù)劲妙,這個(gè)構(gòu)造函數(shù)可以創(chuàng)建實(shí)例化對(duì)象湃鹊,結(jié)果導(dǎo)致方法執(zhí)行時(shí)候this指針的指向會(huì)不斷發(fā)生變化,很難控制是趴。**
** 原因二:javascript里的全局作用域?qū)his指針有很大的影響涛舍,但是javascript里的this在沒(méi)有進(jìn)行new操作也會(huì)生效澄惊,這時(shí)候this往往會(huì)指向全局對(duì)象window唆途。
** 原因三:javascript里call和apply操作符可以隨意改變this指向,這看起來(lái)很靈活掸驱,但是這種不合常理的做法破壞了我們理解this指針的本意肛搬,同時(shí)也讓寫(xiě)代碼時(shí)候很難理解this的真正指向
讓我們先看一段代碼:
this.a = aaa;
console.log(a); //aaa
console.log(this.a); //aaa
console.log(window.a); //aaa
console.log(this); //window
console.log(window); //window
console.log(this == window); //true
console.log(this === window); //true
在script標(biāo)簽里我們可以直接使用this指針,this指針就是window對(duì)象毕贼,我們看到即使使用三等號(hào)它們也是相等的温赔。全局作用域常常會(huì)干擾我們,使的我們不能很好的理解javascript語(yǔ)言的特性,這種干擾的本質(zhì)就是:
** 在javascript語(yǔ)言里全局作用域可以理解為window對(duì)象鬼癣,記住是window是對(duì)象而不是類陶贼,也就是說(shuō)window是被實(shí)例化的對(duì)象,這個(gè)實(shí)例化的過(guò)程是在頁(yè)面加載時(shí)候由javascript引擎完成的待秃,整個(gè)頁(yè)面里的要素都被濃縮到這個(gè)window對(duì)象拜秧,因?yàn)槌绦騿T無(wú)法通過(guò)編程語(yǔ)言來(lái)控制和操作這個(gè)實(shí)例化過(guò)程,所以開(kāi)發(fā)時(shí)候我們就沒(méi)有構(gòu)建這個(gè)this指針的感覺(jué)章郁,常常會(huì)忽視它枉氮,這就是干擾我們?cè)诖a里理解this指針指向window的情形。**
干擾的本質(zhì)還和function的使用有關(guān)暖庄,我們看看下面的代碼:
function ftn01(){
console.log("I am ftn01!");
}
var ftn02 = function(){
console.log("I am ftn02!");
}
上面是我們經(jīng)常使用的兩種定義函數(shù)的方式聊替,第一種定義函數(shù)的方式在javascript語(yǔ)言稱作聲明函數(shù),第二種定義函數(shù)的方式叫做函數(shù)表達(dá)式培廓,這兩種方式我們通常認(rèn)為是等價(jià)的惹悄,但是它們其實(shí)是有區(qū)別的,而這個(gè)區(qū)別常常會(huì)讓我們混淆this指針的使用肩钠,我們?cè)倏纯聪旅娴拇a:
console.log(ftn01);//ftn01() 注意:在firebug下這個(gè)打印結(jié)果是可以點(diǎn)擊泣港,點(diǎn)擊后會(huì)顯示函數(shù)的定義
console.log(ftn02);// undefined
function ftn01(){
console.log("I am ftn01!");
}
var ftn02 = function(){
console.log("I am ftn02!");
}
這又是一段沒(méi)有按順序執(zhí)行的代碼,先看看ftn02蔬将,打印結(jié)果是undefined爷速,undefined我在前文里講到了,在內(nèi)存的棧區(qū)已經(jīng)有了變量的名稱霞怀,但是沒(méi)有棧區(qū)的變量值惫东,同時(shí)堆區(qū)是沒(méi)有具體的對(duì)象,這是javascript引擎在預(yù)處理(群里東方說(shuō)預(yù)處理比預(yù)加載更準(zhǔn)確,我同意他的說(shuō)法廉沮,以后文章里我都寫(xiě)為預(yù)處理)掃描變量定義所致颓遏,但是ftn01的打印結(jié)果很令人意外,既然打印出完成的函數(shù)定義了滞时,而且代碼并沒(méi)有按順序執(zhí)行叁幢,這只能說(shuō)明一個(gè)問(wèn)題:
** 在javascript語(yǔ)言通過(guò)聲明函數(shù)方式定義函數(shù),javascript引擎在預(yù)處理過(guò)程里就把函數(shù)定義和賦值操作都完成了坪稽,在這里我補(bǔ)充下javascript里預(yù)處理的特性曼玩,其實(shí)預(yù)處理是和執(zhí)行環(huán)境相關(guān),在上篇文章里我講到執(zhí)行環(huán)境有兩大類:全局執(zhí)行環(huán)境和局部執(zhí)行環(huán)境窒百,執(zhí)行環(huán)境是通過(guò)上下文變量體現(xiàn)的黍判,其實(shí)這個(gè)過(guò)程都是在函數(shù)執(zhí)行前完成,預(yù)處理就是構(gòu)造執(zhí)行環(huán)境的另一個(gè)說(shuō)法篙梢,總而言之預(yù)處理和構(gòu)造執(zhí)行環(huán)境的主要目的就是明確變量定義顷帖,分清變量的邊界,但是在全局作用域構(gòu)造或者說(shuō)全局變量預(yù)處理時(shí)候?qū)τ诼暶骱瘮?shù)有些不同渤滞,聲明函數(shù)會(huì)將變量定義和賦值操作同時(shí)完成贬墩,因此我們看到上面代碼的運(yùn)行結(jié)果。由于聲明函數(shù)都會(huì)在全局作用域構(gòu)造時(shí)候完成妄呕,因此聲明函數(shù)都是window****對(duì)象的屬性陶舞,這就說(shuō)明為什么我們不管在哪里聲明函數(shù),聲明函數(shù)最終都是屬于window對(duì)象的原因了趴腋。
關(guān)于函數(shù)表達(dá)式的寫(xiě)法還有秘密可以探尋吊说,我們看下面的代碼:
function ftn03(){
var ftn04 = function(){
console.log(this);// window
};
ftn04();
}
ftn03();
運(yùn)行運(yùn)行結(jié)果我們發(fā)現(xiàn)ftn04雖然在ftn03作用域下,但是執(zhí)行它里面的this指針也是指向window优炬,其實(shí)函數(shù)表達(dá)式的寫(xiě)法我們大多數(shù)更喜歡在函數(shù)內(nèi)部寫(xiě)颁井,因?yàn)槁暶骱瘮?shù)里的this指向window這已經(jīng)不是秘密,但是函數(shù)表達(dá)式的this指針指向window卻是常常被我們所忽視蠢护,特別是當(dāng)它被寫(xiě)在另一個(gè)函數(shù)內(nèi)部時(shí)候更加如此雅宾。
其實(shí)在javascript語(yǔ)言里任何匿名函數(shù)都是屬于window對(duì)象,它們也都是在全局作用域構(gòu)造時(shí)候完成定義和賦值葵硕,但是匿名函數(shù)是沒(méi)有名字的函數(shù)變量眉抬,但是在定義匿名函數(shù)時(shí)候它會(huì)返回自己的內(nèi)存地址,如果此時(shí)有個(gè)變量接收了這個(gè)內(nèi)存地址懈凹,那么匿名函數(shù)就能在程序里被使用了蜀变,因?yàn)槟涿瘮?shù)也是在全局執(zhí)行環(huán)境構(gòu)造時(shí)候定義和賦值,所以匿名函數(shù)的this指向也是window對(duì)象介评,所以上面代碼執(zhí)行時(shí)候ftn04的this也是指向window库北,因?yàn)閖avascript變量名稱不管在那個(gè)作用域有效爬舰,堆區(qū)的存儲(chǔ)的函數(shù)都是在全局執(zhí)行環(huán)境時(shí)候就被固定下來(lái)了鏈,變量的名字只是一個(gè)指代而已寒瓦。
這下子壞了情屹,this都指向window,那我們到底怎么才能改變它了杂腰?
先說(shuō)一個(gè)結(jié)論:this都是指向?qū)嵗瘜?duì)象垃你,之前講那么多情況this都指向window,就是因?yàn)檫@些時(shí)候只做了一次實(shí)例化操作喂很,而這個(gè)實(shí)例化都是在實(shí)例化window對(duì)象惜颇,所以this都是指向window。我們要把this從window變成別的對(duì)象恤筛,就得要讓function被實(shí)例化官还,那如何讓javascript的function實(shí)例化呢芹橡?答案就是使用new操作符毒坛。我們看看下面的代碼:
<script type="text/javascript">
//方式一
var obj = {
name:"sharpxiajun",
job:"Software",
show:function(){
console.log("Name:" + this.name + ";Job:" + this.job);
console.log(this);
// Object{ name="sharpxiajun", job="Software", show=function()} }
};
//方式二
var otherObj = new Object();
otherObj.name = "xtq";
otherObj.job = "good";
otherObj.show = function(){
console.log("Name:" + this.name + ";Job:" + this.job);
console.log(this);
// Object { name="xtq", job="good", show=function()} };
obj.show();//Name:sharpxiajun;Job:Software
otherObj.show();//Name:xtq;Job:good
</script>
** 這是我上篇講到的關(guān)于this使用的一個(gè)例子,寫(xiě)法一是我們大伙都愛(ài)寫(xiě)的一種寫(xiě)法林说,里面的this指針不是指向window的煎殷,而是指向Object的實(shí)例,其實(shí)Object就是面向?qū)ο蟮念愅嚷幔罄ㄌ?hào)里就是實(shí)例對(duì)象了豪直,即obj和otherObj。Javascript里通過(guò)字面量方式定義對(duì)象的方式是new Object的簡(jiǎn)寫(xiě)珠移,二者是等價(jià)的弓乙,目的是為了減少代碼的書(shū)寫(xiě)量,可見(jiàn)即使不用new操作字面量定義法本質(zhì)也是new操作符钧惧,所以通過(guò)new改變this指針的確是不過(guò)攻破的真理暇韧。**
下面我貼出在《javascript高級(jí)編程》里對(duì)new操作符的解釋:
new操作符會(huì)讓構(gòu)造函數(shù)產(chǎn)生如下變化:
1.創(chuàng)建一個(gè)新對(duì)象;
2.將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象)浓瞪;
3.執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性)懈玻;
4.返回新對(duì)象
關(guān)于第二點(diǎn)其實(shí)很容易讓人迷惑,例如前面例子里的obj和otherObj乾颁,obj.show()涂乌,里面this指向obj,我以前文章講到一個(gè)簡(jiǎn)單識(shí)別this方式就是看方法調(diào)用前的對(duì)象是哪個(gè)this就指向哪個(gè)英岭,其實(shí)這個(gè)過(guò)程還可以這么理解湾盒,在全局執(zhí)行環(huán)境里window就是上下文對(duì)象,那么在obj里局部作用域通過(guò)obj來(lái)代表了诅妹,這個(gè)和window的理解是一致的罚勾。
第四點(diǎn)也要著重講下,記住構(gòu)造函數(shù)被new操作,要讓new正常作用最好不能在構(gòu)造函數(shù)里寫(xiě)return荧库,沒(méi)有return的構(gòu)造函數(shù)都是按上面四點(diǎn)執(zhí)行堰塌,有了return情況就復(fù)雜了,這個(gè)知識(shí)我會(huì)在講prototype時(shí)候講到分衫。
Javascript還有一種方式可以改變this指針场刑,這就是call方法和apply方法,call和apply方法的作用相同蚪战,就是參數(shù)不同牵现,call和apply的第一個(gè)參數(shù)都是一樣的,但是后面參數(shù)不同邀桑,apply第二個(gè)參數(shù)是個(gè)數(shù)組瞎疼,call從第二個(gè)參數(shù)開(kāi)始后面有許多參數(shù)。Call和apply的作用是什么壁畸,這個(gè)很重要贼急,重點(diǎn)描述如下:
** Call和apply是改變函數(shù)的作用域(有些書(shū)里叫做改變函數(shù)的上下文)**
** Call和apply是將this指針指向方法的第一個(gè)參數(shù)。**
var name = "sharpxiajun";
function ftn(name){
console.log(name);
console.log(this.name);
console.log(this);
}
ftn("101");
// 101
// sharpxiajun
//window
var obj = { name:"xtq" };
ftn.call(obj,"102");
//102
//xtq
//obj
我們看到apply和call改變的是this的指向捏萍,這點(diǎn)在開(kāi)發(fā)里很重要太抓,開(kāi)發(fā)里我們常常被this所迷惑,迷惑的根本原因我在上文講到了令杈,這里我講講表面的原因:
表面原因就是我們定義對(duì)象使用對(duì)象的字面表示法走敌,字面表示法在簡(jiǎn)單的表示里我們很容易知道this指向?qū)ο蟊旧恚沁@個(gè)對(duì)象會(huì)有方法逗噩,方法的參數(shù)可能會(huì)是函數(shù)掉丽,而這個(gè)函數(shù)的定義里也可能會(huì)使用this指針,如果傳入的函數(shù)沒(méi)有被實(shí)例化過(guò)和被實(shí)例化過(guò)异雁,this的指向是不同捶障,有時(shí)我們還想在傳入函數(shù)里通過(guò)this指向外部函數(shù)或者指向被定義對(duì)象本身,這些亂七八糟的情況使用交織在一起導(dǎo)致this變得很復(fù)雜片迅,結(jié)果就變得糊里糊涂残邀。
其實(shí)理清上面情況也是有跡可循的,就以定義對(duì)象里的方法里傳入函數(shù)為例:
** 情形一:傳入的參數(shù)是函數(shù)的別名柑蛇,那么函數(shù)的this就是指向window芥挣;**
** 情形二:傳入的參數(shù)是被new過(guò)的構(gòu)造函數(shù),那么this就是指向?qū)嵗膶?duì)象本身耻台;**
** 情形三:如果我們想把被傳入的函數(shù)對(duì)象里this的指針指向外部字面量定義的對(duì)象空免,那么我們就是用apply和call**
function fn() {
alert(this);
}
var people = {
name: "shuai",
job: "IT",
show: function(fn) {
fn();
}
}
people.show(fn);
function fn() {
this.name = '我是fn';
}
fn.prototype.say = function() {
alert(this.name);
}
var obj = new fn();
var people = {
name: "shuai",
job: "IT",
show: function(obj) {
obj.say();
}
}
people.show(obj);
function fn() {
this.name = '我是fn';
}
fn.prototype.say = function() {
alert(this.name);
}
var obj = new fn();
var people = {
name: "shuai",
job: "IT",
show: function(obj) {
obj.say.call(people);
}
}
people.show(obj);
參考文獻(xiàn):http://www.cnblogs.com/sharpxiajun/p/4148932.html