理解JavaScript中的「this」

對於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

 

this基礎

首先,咱們知道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();
 
再來看一個jquery中this的簡單應用:
$ ("button").click (function (event) {
 // $(this) 的值將是 ($("button")) 對象
 // 由於這個button對象調用了 click () 方法
 console.log ($ (this).prop ("name"));
 });
上面的代碼中,$(this)綁定到了button對象,由於jquery庫將$(this)綁定到調用點擊事件的對象。

 

this中存在的最大的問題

this是沒有被指定值的,直到有一個對象調用了this所在的這個函數(咱們暫且給它一個稱號:this Function)。

也就是說,只有當有一個對象調用了this Function以後,this Fuction中的this才被指定!在大多數狀況下,this Function被調用以後,this都會被指定一個值,可是,也有少數的狀況下this是沒有值的,這個問題咱們將在後面進一步探討。

 

全局做用域(global scope)中的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問題

當包含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

 

解決閉包(closure)中的this問題

另一種容易讓咱們感到困擾的是當咱們使用了一個內部函數(閉包)的時候。首先咱們應該明確的一點就是:閉包中不能經過「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();
內部函數的this不能訪問外部函數的this,因此內部函數的this就指向了全局window對象。爲了解決這個問題,咱們通常都是採用中間變量來保存外部函數的this:
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
 })
 }
上面的代碼中,咱們用一箇中間變量「theUserObj」來保存外部函數的this,以便在forEach內部函數中能夠經過它來調用user對象的屬性。中間變量叫什麼名字取決於你本身,大部分同窗喜歡用「that」,我的以爲這個不夠形象,因此我喜歡用theUserObj這種一看就明白的詞。

 

解決方法做爲變量進行傳遞時的this問題

當咱們把一個對象裏面的函數做爲參數傳遞給另一個對象時,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);

 

解決方法借用中的this問題

何謂方法調用?打個簡單的比方,有兩個對象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/

相關文章
相關標籤/搜索