對於javascript的初學者來講,通常對「this」關鍵字都感到很是迷惑。本文的目的旨在讓你全面的瞭解「this」,理解在每個情景下如何使用「this」,但願經過本文,能夠幫助同窗們不在懼怕「this」!!javascript
其實「this」就是咱們平時用的一個代詞。打個簡單的比喻:html
「小豆豆是一個很幽默的人,他很是喜歡看《暴走漫畫》」java
可是你也能夠這樣寫:jquery
「小豆豆是一個很幽默的人,小豆豆很是喜歡看《暴走漫畫》」瀏覽器
可是平常生活中咱們會一直用這種方式來描述一我的嗎?若是你的回答是Yes,好吧,估計再也沒有人願意跟你作朋友了,我沒騙你…(開個玩笑:-) )。 因此,人類就發明了這樣一種看似高端、洋氣、上檔次的代名詞,說白了javascript中的「this」也就是這麼一個東東。閉包
一個簡單的例子:app
person = {
firstName: "zhou",
lastName: "Quan",
fullName:function(){
alert(person.firstName + " " + person.lastName);//Zhou Quan
alert(this.firstName + " " + this.lastName);//Zhou Quan
}
}
person.fullName();
咱們能夠根據前面所說的方式來理解這個程序的結果, 可是這僅僅停留在表面而已,下面咱們再進行更深一步的理解。ide
首先,咱們知道javascript中全部的函數都擁有屬性,就像一個對象也擁有屬性同樣。當一個函數被執行時,他就擁有了「this」屬性--調用this所在函數的對象。this永遠指向一個獨一無二的對象,這個對象一般包含一個函數/方法,雖然它也能夠被用在一個函數以外的全局做用域中(global scope)。注意:在嚴格模式(strict mode)中,在全局函數或者匿名函數中this的值爲「undefined」(也就是說this沒有綁定任何對象)。函數
「this」被用在一個函數內部(假設是function A),那麼它包含的值是調用「function A」的對象。咱們須要this來訪問調用「function A」的對象的屬性和方法,特別是當咱們不知道到這個對象名叫什麼的時候,或者這個對象沒有名字的時候。其實,this就是調用當前函數的對象的一個代名詞!this
咱們來舉一個簡單的例子:
var person = {
firstName: "zhou",
lastName: "Quan",
//this將爲person對象,由於person對象會調用FullName方法
FullName:function(){
alert(this.firstName + " " + this.lastName); //zhou Quqn
}
}
person.FullName();
$ ("button").click (function (event) {
// $(this) 的值將是 ($("button")) 對象
// 由於這個button對象調用了 click () 方法
console.log ($ (this).prop ("name"));
});
this是沒有被指定值的,直到有一個對象調用了this所在的這個函數(咱們暫且給它一個稱號:this Function)。
也就是說,只有當有一個對象調用了this Function以後,this Fuction中的this才被指定!在大多數狀況下,this Function被調用以後,this都會被指定一個值,可是,也有少數的狀況下this是沒有值的,這個問題咱們將在後面進一步探討。
當一段代碼在瀏覽器中執行時,全部的全局變量和函數都是在「window」對象上定義的。所以,當」this」被用在全局函數中是,他指定的值是」window」對象。咱們來看下面一段代碼:
var firstName = "Peter",
lastName = "Ally";
function showFullName () {
// "this" inside this function will have the value of the window object
// because the showFullName () function is defined in the global scope, just like the firstName and lastName
console.log (this.firstName + " " + this.lastName);
}
var person = {
firstName :"Penelope",
lastName :"Barrymore",
showFullName:function () {
// "this" on the line below refers to the person object, because the showFullName function will be invoked by person object.
console.log (this.firstName + " " + this.lastName);
}
}
showFullName (); // Peter Ally
// window is the object that all global variables and functions are defined on, hence:
window.showFullName (); // Peter Ally
// "this" inside the showFullName () method that is defined inside the person object still refers to the person object, hence:
person.showFullName (); // Penelope Barrymore
當包含this的回調函數做爲參數傳遞時咱們會遇到這樣這個問題:
// We have a simple object with a clickHandler method that we want to use when a button on the page is clicked
var user = {
name:"zhouquan",
age:21,
clickHandler:function (event) {
console.log (this.name + " " + this.age);
}
}
// The button is wrapped inside a jQuery $ wrapper, so it is now a jQuery object
// And the output will be undefined because there is no data property on the button object
$("button").click (user.clickHandler); //undefined
在上面的代碼中,咱們知道$(「button」)是一個對象,」user.clickHandler」做爲click()方法的回調函數。user.clickHandler方法中的this再也不是指向user對象,它如今指向的是調用點擊事件的這個button對象。即便咱們是使用「user.clickHandler」來調用clickHandler方法,可是clickHandler()方法是在button對象做爲上下文(Context)的環境中運行的,因此,this指向的是button對象。
從這一點上咱們能夠看出,當上下文(Context)改變--當咱們在其餘的對象上執行一個方法。「this」所指向的不在是之前的那個對象,而是如今調用這個方法的新的對象。
爲了解決這個問題,咱們可使用apply, call和bind的方法來指定「this」所指向的對象。因此上面代碼的最後一行只需改成:
$("button").click (user.clickHandler.bind (user)); //zhouquan 21
另一種容易讓咱們感到困擾的是當咱們使用了一個內部函數(閉包)的時候。首先咱們應該明確的一點就是:閉包中不能經過「this」關鍵字來訪問外邊函數的this變量,由於閉包中的this他指向的window對象。來看一個例子:
var user = {
country: "china",
data:[
{name:"ZhouYi", age:21},
{name:"ZhouEr", age:22}
],
clickHandler:function(){
//在這個做用域中使用this是OK的,他指向的是user對象
this.data.forEach(function(person){
//可是這個內部函數的this再也不指向user對象
console.log("What is This referring to? " + this); //Object Window
console.log(person.name + " is come from " + this.country);
//ZhouYi is come from undefined
//ZhouEr is come from undefined
})
}
};
user.clickHandler();
clickHandler:function(){
//在這個做用域中使用this是OK的,他指向的是user對象
//咱們定義一個theUserObj來保存this的值
var theUserObj = this;
this.data.forEach(function(person){
//如今咱們再用theUserObj來訪問user對象的屬性就沒問題了
console.log(person.name + " is come from " + theUserObj.country);
//ZhouYi is come from china
//ZhouEr is come from china
})
}
當咱們把一個對象裏面的函數做爲參數傳遞給另一個對象時,this所指定的對象每每會超出咱們的想象,來看個例子吧:
var data = [
{name: "ZhouYi", age:21},
{name: "ZhouEr", age:22}
];
var user = {
//這裏的data屬性是屬於user對象的
data :[
{name:"ZhouYi", age: 31},
{name:"ZhouEr", age: 32}
],
showData:function(){
console.log(this.data[0].name + " " + this.data[0].age) ;
}
}
//把user.showData賦值給一個變量
var showUserData = user.showData;
//當咱們執行這個showUserData函數時,輸出的數據來自全局的data(最外層定義的),而不是來自與user對象中的data.
showUserData(); //ZhouYi 21
解決這個問題的方式跟前面提到的解決回調函數中的this問題相似,咱們仍是採用apply, call和bind的方法來綁定指定的上下文,也就是this該指向哪一個對象。
var showUserDate = user.showData.bind(user);
何謂方法調用?打個簡單的比方,有兩個對象A和B,A、B有相同的屬性,可是B比A還多了一個方法,這個方法是對B中的數據進行處理,如今咱們想把B這種方法也給A用用:
var gameController = {
scores : [20, 34, 55, 46, 77],
avgScore: null,
players:[
{name:"Tommy", playerID:987, age:23},
{name:"Pau", playerID: 87, age: 33}
]
}
var appController = {
scores: [900, 845, 809, 950],
avgScore: null,
avg: function(){
var sumOfSores = this.scores.reduce(function(prev, cur, index, array){
return prev + cur;
});
this.avgScore = sumOfSores /this.scores.length;
}
}
//因爲avg()是被appController調用的,因此avg中的this指向的是appController對象。
gameController.avgScore = appController.avg();
console.log(gameController.avgScore); //undefined
如何解決這個問題呢? 爲了讓appController.avg()指向gameController對象,咱們可使用apply()方法:
// Note that we are using the apply () method, so the 2nd argument has to be an array—the arguments to pass to the appController.avg () method.
appController.avg.apply (gameController, gameController.scores);
// The avgScore property was successfully set on the gameController object, even though we borrowed the avg () method from the appController object
console.log (gameController.avgScore); // 46.4
// appController.avgScore is still null; it was not updated, only gameController.avgScore was updated
console.log (appController.avgScore); // null
注意咱們這裏使用了兩個參數,第一個參數表示this的上下文,也就是this所指向的對象;第二個參數表示要進行運算的數據。固然啦,咱們還能夠經過這種方式達到一樣的效果:
gameController.avg = appController.avg;
gameController.avg();
但願我寫的這些東西能夠給你帶來一些幫助,若是你以爲哪一個地方存在不足,請你在評論處提出來,我將及時完善,省得誤導後面的同窗;若是你以爲這篇文章講得還能夠,請幫我推薦給更多的有須要的同窗閱讀,謝謝!
參考資料:http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/