javascript中this指針探討


  javascript是一門類java語言有很多跟java相類似的特點,但也僅是類似而已,真正使用中還是有很大的差別。this指針常常讓很多初學者抓狂,本人也曾為此困惑不解,查找過很多資料,今天在這里總結一下,希望能幫助后來者更快馴服這只攔路虎。網上有很多講解this指針的文章其中不乏精品,以我看來了解this指針關鍵在於掌握javascript中函數的四種調用模式。那么什么是調用?調用指的是跟在任何產生一個函數值的表達式之后使用"()",obj.f()這種方式成為調用,obj.f這種方式稱為訪問。

  一、方法調用模式:

  在該調用模式中函數作為一個對象的方法被調用:obj.fun()。當函數以此種形式被調用時this指針綁定到調用該方法的對象上即obj。

 1 var myObj = {
 2   value: 0,
 3   increase: function(){
 4     this.value++;
 5     console.log(this.value);
 6   }
 7 };
 8 // this綁定到myObj對象上
 9 myObj.increase(); // 1
10 
11 myObj2 = {
12   value: 10
13 };
14 myObj2.increase = myObj.increase;// myObj2與myObj的increase指向同一函數引用地址
15 // this綁定到myObj2對象上
16 myObj2.increase(); // 11

  所以在將一個html元素的某一事件綁定某一函數時,若函數中使用this指針,則this指針會被綁定到該元素上。

1 var ele1 = document.getElementById('id');
2 ele1.addEventListener('click', myObj.increase, false);
3 // 該綁定相當於
4 ele1.onclick = myObj.increase;
5 // 事件觸發時即相當於調用click方法
6 ele1.click(); // 與上文中myObj2.increase一個道理,若ele1中沒有value屬性則會報錯

 

  二、函數調用模式:

  當函數沒有當做方法調用即沒有被一個對象通過點語法,這時它被當做一個函數來調用。以此模式調用函數時,this被綁定到全局對象。

 1 var value = -10;
 2 var myObj = {
 3   value: 0,
 4   increase: function(){
 5     this.value++;
 6     console.log(this.value);
 7   }
 8 };
 9 // this綁定到myObj對象上
10 myObj.increase(); // 1
11 
12 var gInc = myObj.increase;
13 gInc(); // -9 this綁定到window對象上

  所以在方法中使用內部函數時要特別注意,下例中other函數中的this並未綁定到myObj對象上

var value = -10;
var myObj = {
  value: 0,
  increase: function(){
    this.value++;
    console.log(this.value);
    var other = function(){
      this.value++;
      console.log(this.value); 
    };
    other();// -9, 這時other中的this綁定到window對象上
  }
};
// this綁定到myObj對象上
myObj.increase(); // 1

var gInc = myObj.increase;
gInc(); // -8
///////////////
//1
//-9
//-8
//-7

  幸運的是我們可以使用以下方式來在other中訪問myObj對象:

 1 var myObj = {
 2   value: 0,
 3   increase: function(){
 4     this.value++;
 5     console.log(this.value);
 6     var that = this;
 7     var other = function(){
 8       that.value++;
 9       console.log(that.value); 
10     };
11     other();// -9, 這時other中的this綁定到window對象上
12   }
13 };
14 // this綁定到myObj對象上
15 myObj.increase(); // 1
16 //////////////
17 //1
18 //2

  作為瀏覽器兼容性中很重要的一條:element.attachEvent綁定事件方法,當該事件觸發時this指針並未綁定到element對象上,而是綁定到了window對象上,原因在於IE中事件觸發時,響應函數是以一個獨立函數即函數調用模式來調用的

 

  三、構造器模式調用:

  如果在一個函數前面加上new調用則成為構造器模式調用。使用new運算符,會產生一個連接到該函數prototype的新對象,this指針則被綁定到這個新對象上。

1 var P = function(n){
2   this.name = n;
3 }
4 P.prototype.getName = function(){
5   console.log(this.name);
6 }
7 var p = new P('woodtree'); // 這時P中的this對象唄綁定p指向的對象引用上
8 p.getName();// woodtree

  同時new運算符還會改變函數的返回值。以函數調用模式調用P即P()返回undefined,而通過new運算符則返回一個新對象。new運算符類似於以下Function.prototype.create函數:

 1 var P = function(n){
 2   this.name = n;
 3 }
 4 P.prototype.getName = function(){
 5   console.log(this.name);
 6 }
 7 var p = new P('woodtree');
 8 p.getName();
 9 
10 Object.prototype.create = Object.create || function(proto){
11   var F = function(){};
12   F.prototype = proto;
13   return new F();
14 }
15 
16 Function.prototype.create = function(){
17   var that = object.create(this.prototype);
18   var obj = this.apply(that, arguments);
19 
20   return obj || that;
21 }
22 var p2 = P.create('hello new');
23 p2.getName(); // hello new

 

  四、apply、call模式調用:

  javascript中函數對象繼承自Object亦可以擁有方法。call跟apply允許我們選擇this的綁定對象。這兩個方法的區別在於apply第二個參數必須是一個數組或者類數組對象即擁有length屬性(arguments、NodeList、HTMLElementCollection等),而call除了第一個參數為要綁定this的對象外,后可跟無數的參數,參數之間用逗號間隔。

 1 var P = function(n){
 2   this.name = n;
 3 }
 4 P.prototype.getNameAndAge = function(age){
 5   console.log(this.name + age);
 6 }
 7 
 8 P.prototype.getNameAndAge.call({
 9   name: 'catboat'
10 }, 99);
11 P.prototype.getNameAndAge.apply({
12   name: 'lanuch'
13 }, [99]);

 

  通過使用apply與call方法我們可以自行實現ES5中的bind函數

 1 var P = function(n){
 2   this.name = n;
 3 }
 4 P.prototype.getNameAndAge = function(age, age2){
 5   console.log(this.name + age + age2);
 6 }
 7 
 8 P.prototype.getNameAndAge.call({
 9   name: 'catboat'
10 }, 99);
11 P.prototype.getNameAndAge.apply({
12   name: 'lanuch'
13 }, [99]);
14 
15 Function.prototype.bindContext = function(that){
16   var _method = this,
17   slice = Array.prototype.slice,
18   args = slice.call(arguments, 1);
19 
20   return function(){
21     _method.apply(that, Array.prototype.concat.apply(args, arguments));
22   }
23 };
24 
25 var f1 = P.prototype.getNameAndAge.bindContext({
26   name: 'barque'
27 }, 88);
28 f1(9);

  同樣我們也可以解決IE中事件觸發時,this指針問題(以下代碼來自《Javascript框架設計》):

1 var addEvent = document.addEventListener ? function(el, type, fn, capture){
2   return el.addEventListener(type, fn, capture);
3 } : function(el, type, fn){
4   el.attachEvent('on' + type, fn.bindContext(el, event));
5 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM