首頁 > web前端 > js教程 > 跟我學習javascript的this關鍵字_javascript技巧

跟我學習javascript的this關鍵字_javascript技巧

WBOY
發布: 2016-05-16 15:31:09
原創
981 人瀏覽過

本文僅就這一問題展開討論,閱罷本文,讀者若能正確回答JavaScript 中的What 's this 問題,作為作者,我就會覺得花費這麼多功夫,撰寫這樣一篇文章是值得的。

我們要記住一句話:this永遠指向函數運行時所在的物件!而不是函數被創建時所在的物件。也即:誰調用,指向誰。 切記…

本文將分三種情況來分析this物件到底身處何處。
1、普通函數中的this
無論this身處何處,第一要務就是要找到函數運行時的位置。

 var name="全局";
 function getName(){
   var name="局部";
   return this.name;
 };
 alert(getName());
登入後複製

當this出現在全域環境的函數getName中時,此時函數getName在運行時的位置在

alert(getName());
登入後複製

顯然,函數getName所在的物件是全域對象,也就是window,因此this的安身之處定然在window。此時的this指向window對象,getName回傳的this.name其實是window.name,所以alert出來的是「全域」!

那麼,當this不是出現在全域環境的函數中,而是出現在局部環境的函數中時,又會身陷何方呢?

 var name="全局";
 var xpg={
   name:"局部",
   getName:function(){
     return this.name;
   }
 };
 alert(xpg.getName());
登入後複製

其中this身處的函數getName不是在全域環境中,而是處在xpg環境中。無論this身處何處,一定要找到函數運行時的位置。此時函數getName運行時的位置

alert(xpg.getName());
登入後複製

顯然,函數getName所在的物件是xpg,因此this的安身之處定然在xpg,即指向xpg對象,則getName返回的this.name其實是xpg.name,因此alert出來的是「局部」!

舉例鞏固一下:

var someone = {
  name: "Bob",
  showName: function(){
    alert(this.name);
  }
};

var other = {
  name: "Tom",
  showName: someone.showName
}

other.showName();  //Tom

登入後複製

this關鍵字雖然是在someone.showName中聲明的,但運行的時候是other.showName,所以this指向other.showName函數的當前對象,即other,故最後alert出來的是other.name。

2、閉包中的this

閉包也是個不安分子,本文暫且不對其過於贅述,簡而言之:所謂閉包就是在一個函數內部創建另一個函數,且內部函數訪問了外部的變數。
浪子this與痞子閉包混在一起,可見將永無寧日啊!

 var name="全局";
 var xpg={
   name:"局部",
   getName:function(){
     return function(){
       return this.name;
     };
   }
 };
 alert(xpg.getName()());
登入後複製

此時的this明顯身處困境,竟然處在getName函數中的匿名函數裡面,而該匿名函數又調用了變數name,因此構成了閉包,即this身處閉包中。
無論this身處何處,一定要找到函數運行時的位置。此時不能根據函數getName執行時的位置來判斷,而是根據匿名函數的執行時間位置來判斷。

function (){
  return this.name;
};

登入後複製

顯然,匿名函數所在的物件是window,因此this的安身之處定然在window,則匿名函數傳回的this.name其實是window.name,因此alert出來的就是「全域」!

那麼,如何在閉包中使得this身處在xpg中呢? —緩存this

 var name="全局";
 var xpg={
   name:"局部",
   getName:function(){
     var that=this;
     return function(){
       return that.name;
     };
   }
 };
 alert(xpg.getName()());
登入後複製

在getName函數中定義that=this,此時getName函數運行時位置在

alert(xpg.getName());
登入後複製

則this指向xpg對象,因此that也指向xpg對象。在閉包的匿名函數中回傳that.name,則此時回傳的that.name其實是xpg.name,因此就可以alert出來 「局部」!

3、new關鍵字建立新物件

new關鍵字後的建構子中的this指向用該建構子建構出來的新物件:

function Person(__name){
  this.name = __name;    //这个this指向用该构造函数构造的新对象,这个例子是Bob对象
}
Person.prototype.show = function(){
  alert(this.name);  //this 指向Person,this.name = Person.name;
}

var Bob = new Person("Bob");
Bob.show();    //Bob

登入後複製

4、call與apply中的this

在JavaScript中能管的住this的估計也就非call與apply莫屬了。
call與apply就像this的父母一般,讓this住哪它就得住哪,不得不聽話!當無參數時,目前物件為window

var name="全局";
var xpg={
  name:"局部"
};
function getName(){
  alert(this.name);
}
getName(xpg);
getName.call(xpg);
getName.call();
登入後複製

其中this身處函數getName中。無論this身處何處,一定要找到函數運行時的位置。此時函數getName運行時的位置

getName(xpg);
登入後複製

顯然,函數getName所在的物件是window,因此this的安身之處定然在window,即指向window對象,則getName返回的this.name其實是window.name,因此alert出來的是「全域」!

那麼,該call與apply登場了,因為this必須聽他們的指揮!
getName.call(xpg);
其中,call指定this的安身之處就是在xpg對象,因為this被迫只能在xpg那安家,則此時this指向xpg對象, this.name其實是xpg.name,因此alert出來的是“局部” !

5、eval中的this

對於eval函數,其執行時候似乎沒有指定當前對象,但實際上其this並非指向window,因為該函數執行時的作用域是當前作用域,即等同於在該行將裡面的程式碼填進去。下面的例子說明了這個問題:

var name = "window";

var Bob = {
  name: "Bob",
  showName: function(){
    eval("alert(this.name)");
  }
};

Bob.showName();  //Bob

登入後複製

6、没有明确的当前对象时的this

当没有明确的执行时的当前对象时,this指向全局对象window。
例如对于全局变量引用的函数上我们有:

var name = "Tom";

var Bob = {
  name: "Bob",
  show: function(){
    alert(this.name);
  }
}

var show = Bob.show;
show();  //Tom

登入後複製

你可能也能理解成show是window对象下的方法,所以执行时的当前对象时window。但局部变量引用的函数上,却无法这么解释:

var name = "window";

var Bob = {
  name: "Bob",
  showName: function(){
    alert(this.name);
  }
};

var Tom = {
  name: "Tom",
  showName: function(){
    var fun = Bob.showName;
    fun();
  }
};

Tom.showName();  //window

登入後複製

在浏览器中setTimeout、setInterval和匿名函数执行时的当前对象是全局对象window,这条我们可以看成是上一条的一个特殊情况。

var name = "Bob"; 
var nameObj ={ 
   name : "Tom", 
   showName : function(){ 
     alert(this.name); 
   }, 
   waitShowName : function(){ 
     setTimeout(this.showName, 1000); 
   } 
 }; 

 nameObj.waitShowName();

登入後複製

所以在运行this.showName的时候,this指向了window,所以最后显示了window.name。

7、dom事件中的this

(1)你可以直接在dom元素中使用

<input id="btnTest" type="button" value="提交" onclick="alert(this.value))" />
登入後複製

分析:对于dom元素的一个onclick(或其他如onblur等)属性,它为所属的html元素所拥有,直接在它触发的函数里写this,this应该指向该html元素。

(2)给dom元素注册js函数
a、不正确的方式

<script type="text/javascript">
 function thisTest(){
 alert(this.value); // 弹出undefined, this在这里指向&#63;&#63;
}
</script>
<input id="btnTest" type="button" value="提交" onclick="thisTest()" />
登入後複製

分析:onclick事件直接调用thisTest函数,程序就会弹出undefined。因为thisTest函数是在window对象中定义的,
所以thisTest的拥有者(作用域)是window,thisTest的this也是window。而window是没有value属性的,所以就报错了。
b、正确的方式

<input id="btnTest" type="button" value="提交" />

<script type="text/javascript">
 function thisTest(){
 alert(this.value); 
}
document.getElementById("btnTest").onclick=thisTest; //给button的onclick事件注册一个函数
</script>

登入後複製

分析:在前面的示例中,thisTest函数定义在全局作用域(这里就是window对象),所以this指代的是当前的window对象。而通过document.getElementById(“btnTest”).onclick=thisTest;这样的形式,其实是将btnTest的onclick属性设置为thisTest函数的一个副本,在btnTest的onclick属性的函数作用域内,this归btnTest所有,this也就指向了btnTest。其实如果有多个dom元素要注册该事件,我们可以利用不同的dom元素id,用下面的方式实现:

document.getElementById("domID").onclick=thisTest; //给button的onclick事件注册一个函数。
登入後複製

因为多个不同的HTML元素虽然创建了不同的函数副本,但每个副本的拥有者都是相对应的HTML元素,各自的this也都指向它们的拥有者,不会造成混乱。
为了验证上述说法,我们改进一下代码,让button直接弹出它们对应的触发函数:

<input id="btnTest1" type="button" value="提交1" onclick="thisTest()" />
<input id="btnTest2" type="button" value="提交2" />

<script type="text/javascript">
function thisTest(){
this.value="提交中";
}
var btn=document.getElementById("btnTest1");
alert(btn.onclick); //第一个按钮函数

var btnOther=document.getElementById("btnTest2");
btnOther.onclick=thisTest;
alert(btnOther.onclick); //第二个按钮函数
</script>
其弹出的结果是:

//第一个按钮
function onclick(){
 thisTest()
}

//第二个按钮
function thisTest(){
 this.value="提交中";
}

登入後複製

从上面的结果你一定理解的更透彻了。
By the way,每新建一个函数的副本,程序就会为这个函数副本分配一定的内存。而实际应用中,大多数函数并不一定会被调用,于是这部分内存就被白白浪费了。所以我们通常都这么写:

<input id="btnTest1" type="button" value="提交1" onclick="thisTest(this)" />
<input id="btnTest2" type="button" value="提交2" onclick="thisTest(this)" />
<input id="btnTest3" type="button" value="提交3" onclick="thisTest(this)" />
<input id="btnTest4" type="button" value="提交4" onclick="thisTest(this)" />

<script type="text/javascript">
 function thisTest(obj){
 alert(obj.value); 
}
</script>

登入後複製

这是因为我们使用了函数引用的方式,程序就只会给函数的本体分配内存,而引用只分配指针。这样写一个函数,调用的地方给它分配一个(指针)引用,这样效率就高很多。当然,如果你觉得这样注册事件不能兼容多种浏览器,可以写下面的注册事件的通用脚本:

//js事件 添加 EventUtil.addEvent(dom元素,事件名称,事件触发的函数名) 移除EventUtil.removeEvent(dom元素,事件名称,事件触发的函数名)
var EventUtil = new eventManager();

//js事件通用管理器 dom元素 添加或者移除事件
function eventManager() {
  //添加事件
  //oDomElement:dom元素,如按钮,文本,document等; ****** oEventType:事件名称(如:click,如果是ie浏览器,自动将click转换为onclick);****** oFunc:事件触发的函数名
  this.addEvent = function(oDomElement, oEventType, oFunc) {
    //ie
    if (oDomElement.attachEvent) {
      oDomElement.attachEvent("on" + oEventType, oFunc);
    }
    //ff,opera,safari等
    else if (oDomElement.addEventListener) {
      oDomElement.addEventListener(oEventType, oFunc, false);
    }
    //其他
    else {
      oDomElement["on" + oEventType] = oFunc;
    }
  }

  this.removeEvent = function(oDomElement, oEventType, oFunc) {
    //ie
    if (oDomElement.detachEvent) {
      oDomElement.detachEvent("on" + oEventType, oFunc);
    }
    //ff,opera,safari等
    else if (oDomElement.removeEventListener) {
      oDomElement.removeEventListener(oEventType, oFunc, false);
    }
    //其他
    else {
      oDomElement["on" + oEventType] = null;
    }
  }
}

登入後複製

正像注释写的那样,要注册dom元素事件,用EventUtil.addEvent(dom元素,事件名称,事件触发的函数名)即可, 移除时可以这样写:EventUtil.removeEvent(dom元素,事件名称,事件触发的函数名),这是题外话,不说了。

 以上就是本文的全部内容,希望通过这篇文章大家更加了解javascript的this关键字,大家共同进步。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板