于程序員來說,this這個關(guān)鍵字應(yīng)該是再熟悉不過了,在C++、C#以及Java中,它的身影隨處可見,this也可以說是編程語言JavaScript的精髓,但是一定也有不少JavaScript開發(fā)者都遇到過this的陷阱。
在C++、C#、Java中,this擁有十分明確的含義,就是單純指向當(dāng)前的對象實例,但是,在動態(tài)編程語言JavaScript中,相同的this寫法,卻有不同的含義。
JavaScript中的this代表函數(shù)運行時自動生成的一個內(nèi)部對象,只能在函數(shù)內(nèi)部使用。并且,你不能在創(chuàng)建統(tǒng)計this的時候就確定它,而應(yīng)該在函數(shù)執(zhí)行時進行確定,所以,一不留神,你就會掉進意想不到的坑!
0、誰調(diào)用了就誰來負責(zé)
我們先來看看代碼:
var name='a';
var obj={
name: 'b',
getName: function() {
console.log(this.name);
}
};
obj.getName(); // b
var getName=obj.getName;
getName(); // a
var obj2=(function() {
return function() {
console.log(this.name);
}
})();
obj2(); // a
首先,obj.getName()打印出來b,因為,obj調(diào)用了getName(),所以其this指向obj,this.name就是b。
其次,getName()打印出a的原因:理由是此時的obj并不調(diào)用getName(),而是全局變量window,因此,結(jié)果是a
最后,obj2中,因為采用了立即執(zhí)行函數(shù),它返回一個函數(shù),而當(dāng)我們調(diào)用時,調(diào)用它的也是全局變量window,所以打印結(jié)果也是a。
需要注意的是,匿名函數(shù)內(nèi)的this是指向window的,更準確的說是指向調(diào)用者。
1、必須了解new的過程
function Test() {
console.log(this.name);
}
Test(); // a
var test=new Test(); // undefined
function Test2() {
this.name='c';
}
var test2=new Test2();
console.log(test2.name); // c
為什么這個例子最終打印出來的是c呢?這里new的過程不可忽略,new Test2() 其實執(zhí)行了三步動作:
首先,創(chuàng)建一個空對象obj(var obj={};),然后,將這個空對象的[[Prototype]](__proto__)成員指向了Test2函數(shù)對象prototype成員對象最后,將Test2函數(shù)對象的this指針替換成obj:
obj.__proto__=Test2.prototype;
Test2.call(obj);
當(dāng)沒有顯式返回值或者返回為基本類型、null時,默認將 this 返回(參考2)
因此,在這個例子中,當(dāng)你使用new的時候,函數(shù)內(nèi)部的this指向已經(jīng)改變了,不再指向window。
主要應(yīng)該搞清楚new的過程,然后就知道打印出c的原因了。
2、奇怪的return
看過上面的例子后,你是否覺得已經(jīng)掌握了new的函數(shù)內(nèi)的this指向?千萬別掉以輕心,在JavaScript中,多的是例外,如:
function Test3() {
this.name='d';
return {};
}
var test3=new Test3();
console.log(test3.name); // undefined
function Test4() {
this.name='d';
return 1;
}
var test4=new Test4();
console.log(test4.name); // d
可以再試試return 'd' | null | undefined中的一個或更多類型。
結(jié)論:如果return的是一個對象(null除外),那么this指向的這個對象,否則this依舊指向?qū)嵗龑ο蟆?/p>
當(dāng)下 Web 編程大行其道時,JavaScript無疑是使用最為廣泛的前端語言。JavaScript為我們提供了極佳的體驗,同時,我們在使用JavaScript時,又經(jīng)常會因為JavaScript的一些奇怪的特性所坑。當(dāng)然我們可能用一些簡單的方法即可避免這些問題。通過這些簡單的做法,你可以使JavaScript成為一門更好的語言,也讓你自己成為一個更好的程序員。
==、!=操作符
JavaScript有兩組相等運算符:===和!==,以及它們邪惡的李生兄弟==和!=。===和!==這一組運算符會按照你期望的方式工作。如果兩個運算數(shù)類型一致且擁有相同的值,那么===返回true, !==返回false。而它們邪惡的孿生兄弟只有在兩個運算數(shù)類型一致時才會做出正確的判斷,如果兩個運算數(shù)是不同的類型,它們試圖去強制轉(zhuǎn)換值的類型。轉(zhuǎn)換的規(guī)則復(fù)雜且難以記憶。這里有一些有趣的例子:
''=='0' //false
0=='' //true
0=='0' //true
false=='false' //false
false=='0' //true
false==undefined //false
false==null //false
null==undefined //true
' \t\r\n'==0 //true
==運算符對傳遞性的缺乏值得我們警惕。我的建議是永遠不要使用那對邪惡的孿生兄弟。相反,請始終使用===和 !==。如果以上所有的比較使用===運算符,結(jié)果都是 false。
with 語句
JavaScript提供了一個with語句,本意是想用它來快捷地訪問對象的屬性。不幸的是,它的結(jié)果可能有時不可預(yù)料,所以應(yīng)該避免使用它。下面的語句:
with (obj) { a * b;}
和下面的代碼做的是同樣的事情:
if (obj.a===undefined) {
a * obj.b===undefined ? b : obj.b;
} else {
obj.a=obj.b===undefined ? b : obj.b;
}
所以,它等于這些語句中的某一條:
a=b
a=obj.b;
obj .a=b;
obj.a=obj.b;
通過閱讀程序代碼,你不可能辨別出你會得到的是這些語句中的哪一條。它可能隨著程序運行到下一步時發(fā)生變化。它甚至可能在程序運行過程中就發(fā)生了變化。如果你不能通過閱讀程序而了解它將會做什么,你就無法確信它會正確地做你想要做的事情。
With語句在這門語言里存在,本身就嚴重影響了 JavaScript處理器的速度,因為它阻斷了變量名的詞法作用域綁定。它的本意是好的,但如果沒有它,JavaScript語言會更好一點。
eval 語句
eval函數(shù)傳遞一個字符串給JavaScript編譯器,并且執(zhí)行其結(jié)果。它是一個被濫用得最多的JavaScript特性。那些對JavaScript語言一知半解的人們最常用到它。例如,如果你知道點表示法,但不知道下標表示法,就可能會這么寫:
eval ("myValue=myObject." + myKey + ";")
而不是這么寫:
myvalue=myObject[myKey];
使用eval形式的代碼更加難以閱讀。這種形式使得性能顯著降低,因為它需要運行編譯器,但也許只是為了執(zhí)行一個微不足道的賦值語句。它也會讓JSLint 失效,讓此工具檢測問題的能力大打折扣。eval函數(shù)還減弱了你的應(yīng)用程序的安全性,因為它給被求值的文本授予了太多的權(quán)力。而且就像with語句執(zhí)行的方式一樣,它降低了語言的性能。Function構(gòu)造器是eval的另一種形式,同樣也應(yīng)該避免使用它。瀏覽器提供的setTimeout和setlnterval函數(shù),它們能接受字符串參數(shù)或函數(shù)參數(shù)。當(dāng)傳遞的是字符串參數(shù)時,setTimeout和setlnterval會像eval那樣去處理。同樣也應(yīng)該避免使用字符串參數(shù)形式。
位運算符
JavaScript有著與Java相同的一套位運算符:
& and按位與
| or按位或
^ xor按位異或
~ not按位非
>> 帶符號的右位移
>>> 無符號的(用0補足的) 右位移
<< 左位移
在Java里,位運算符處理的是整數(shù)。JavaScript沒有整數(shù)類型,它只有雙精度的浮點數(shù)。因此,位操作符把它們的數(shù)字運算數(shù)先轉(zhuǎn)換成整數(shù),接著執(zhí)行運算,然后再轉(zhuǎn)換回去。在大多數(shù)語言中,這些位運算符接近于硬件處理,所以非??臁5獼avaScript的執(zhí)行環(huán)境一般接觸不到硬件,所以非常慢。JavaScript很少被用來執(zhí)行位操作。還有,在JavaScript程序中,& 非常容易被誤寫為 && 運算符。位運算符出現(xiàn)在JavaScript中降低了這門語言的冗余,使得bug更容易被隱藏起來。
function 函數(shù)對比 function 表達式
JavaScript既有function語句,同時也有function表達式。這令人困惑,因為它們看起來好像就是相同的。一個function語句就是其值為一個函數(shù)的var語句的速記形式。
下面的語句:
function foo () {}
意思相當(dāng)于:
var foo=function foo () {};
我們建議使用的是第2種形式,因為它能明確表示foo是一個包含一個函數(shù)值的變量。要用好這門語言,理解函數(shù)就是數(shù)值是很重要的。
function語句在解析時會發(fā)生被提升的情況。這意味著不管function被放置在哪里,它會被移動到被定義時所在作用域的頂層。這放寬了函數(shù)必須先聲明后使用的要求,而我認為這會導(dǎo)致混亂。在if語句中使用function語句也是被禁止的。結(jié)果表明大多數(shù)的瀏覽器都允許在if語句里使用function語句,但它們在解析時的處理上各不相同。這就造成了可移植性的問題。
一個語句不能以一個函數(shù)表達式開頭,因為官方的語法假定以單詞 function 開頭的語句是一個function語句。解決方法就是把函數(shù)調(diào)用括在一個圓括號之中。
(function () {
var hidden_variable;
//這個函數(shù)可能對環(huán)境有一些影響,但不會引入新的全局變量,
})();
于程序員來說,this這個關(guān)鍵字應(yīng)該是再熟悉不過了,在C++、C#以及Java中,它的身影隨處可見,this也可以說是編程語言JavaScript的精髓,但是一定也有不少JavaScript開發(fā)者都遇到過this的陷阱。
在C++、C#、Java中,this擁有十分明確的含義,就是單純指向當(dāng)前的對象實例,但是,在動態(tài)編程語言JavaScript中,相同的this寫法,卻有不同的含義。
JavaScript中的this代表函數(shù)運行時自動生成的一個內(nèi)部對象,只能在函數(shù)內(nèi)部使用。并且,你不能在創(chuàng)建統(tǒng)計this的時候就確定它,而應(yīng)該在函數(shù)執(zhí)行時進行確定,所以,一不留神,你就會掉進意想不到的坑!
0、誰調(diào)用了就誰來負責(zé)
我們先來看看代碼:
var name='a';
var obj={
name: 'b',
getName: function() {
console.log(this.name);
}
};
obj.getName(); // b
var getName=obj.getName;
getName(); // a
var obj2=(function() {
return function() {
console.log(this.name);
}
})();
obj2(); // a
首先,obj.getName()打印出來b,因為,obj調(diào)用了getName(),所以其this指向obj,this.name就是b。
其次,getName()打印出a的原因:理由是此時的obj并不調(diào)用getName(),而是全局變量window,因此,結(jié)果是a
最后,obj2中,因為采用了立即執(zhí)行函數(shù),它返回一個函數(shù),而當(dāng)我們調(diào)用時,調(diào)用它的也是全局變量window,所以打印結(jié)果也是a。
需要注意的是,匿名函數(shù)內(nèi)的this是指向window的,更準確的說是指向調(diào)用者。
1、必須了解new的過程
function Test() {
console.log(this.name);
}
Test(); // a
var test=new Test(); // undefined
function Test2() {
this.name='c';
}
var test2=new Test2();
console.log(test2.name); // c
為什么這個例子最終打印出來的是c呢?這里new的過程不可忽略,new Test2() 其實執(zhí)行了三步動作:
首先,創(chuàng)建一個空對象obj(var obj={};),然后,將這個空對象的[[Prototype]](__proto__)成員指向了Test2函數(shù)對象prototype成員對象最后,將Test2函數(shù)對象的this指針替換成obj:
obj.__proto__=Test2.prototype;
Test2.call(obj);
當(dāng)沒有顯式返回值或者返回為基本類型、null時,默認將 this 返回(參考2)
因此,在這個例子中,當(dāng)你使用new的時候,函數(shù)內(nèi)部的this指向已經(jīng)改變了,不再指向window。
主要應(yīng)該搞清楚new的過程,然后就知道打印出c的原因了。
2、奇怪的return
看過上面的例子后,你是否覺得已經(jīng)掌握了new的函數(shù)內(nèi)的this指向?千萬別掉以輕心,在JavaScript中,多的是例外,如:
function Test3() {
this.name='d';
return {};
}
var test3=new Test3();
console.log(test3.name); // undefined
function Test4() {
this.name='d';
return 1;
}
var test4=new Test4();
console.log(test4.name); // d
可以再試試return 'd' | null | undefined中的一個或更多類型。
結(jié)論:如果return的是一個對象(null除外),那么this指向的這個對象,否則this依舊指向?qū)嵗龑ο蟆?/p>
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。