天天看點

JavaScript中的this、call、apply

1、this

JavaScript中的this,總是指向一個對象 ,而具體指向哪個對象是在運作時基于函數的執行環境動态綁定的,而非函數被聲明時的環境。

this的指向大緻可以分以下4種:

  • 作為對象的方法調用
  • 作為普通函數調用
  • 構造器調用
  • Function.prototype.call或Function.prototype.apply調用

1、作為對象的方法調用

var obj =  {
         name:"Tome",
         getName:function(){
           alert(this == obj); // true
           alert(this.name);  // Tome
         }
      };
obj.getName();      

因為obj字面量對象調用了getiName()方法,是以this就代表了obj字面量對象。字面量對象一建立就在記憶體中有自己的空間。

2、作為普通函數調用

當函數不作為對象的屬性被調用時,此時this總是指向全局對象。在浏覽器的JavaScript中,這個全局對象就是window對象。

var name = "globalName";
var getName = function(){
    return this.name;
};
alert(getName());      

window.name = "globalNameA";
var getName = function(){
      return this.name;
};
alert(getName());      

window.name = "globalNameB";
var myObject = {
    name:"Jhony",
    getName:function(){
        return this.name;
         }
   }
var getName = myObject.getName;
alert(getName()); // globalNameB
alert(myObject.getName()); // Jhony      

var getName = myObject.getName這一句隻是把myObject字面的對象方法借來用。而裡面用到的this是根據具體的運作環境來綁定的,是以當在myObject字面量對象外且不作為myObject的屬性來調用getName方法時,this指向全局對象。當作為myObject對象的屬性來調用時,即myObject.getName()調用時,this又指向字面量對象myObject。

注意:有個很趣的現象

window.id = "window";
document.getElementById("root").onclick = function(){
    alert(this.id); // root
    var callback = function(){
        alert(this.id);
    };
    callback(); // window
}      

此時callback裡的this是指向了全局對象window而不是id為root的元素對象,為什麼會這樣呢,因為callback方法前面沒有指明調用對象,callback方法不是作為id為root元素對象的屬性來調用的,是以this預設指向全局對象。如果你要callback方法裡的this指向id為root元素,怎麼辦?可以通過call或apply方法,重新指定this的指向,如:

.id = "window";
      document.getElementById("root").onclick = function(){
        alert(this.id); // root
        var callback = function(){
          alert(this.id);
        };
        callback.call(document.getElementById("root")); // root
        callback.apply(document.getElementById("root")); // root
      }      

3、構造器調用

JavaScript中沒有類,但是可以從構造器中建立對象,同時也提供了new運算符,但他們隻是用來申請一個記憶體空間,然後通過這個構造器方法來對這個空間進行指派。

var MyClass = function(name){
      this.name = name;
    }

    function MyClass1(name){
      this.name = name;
    } 

    var myClass = new MyClass("Tome");
    alert(myClass.name)
    var myClass1 = new MyClass1("Haha");
    alert(myClass1.name)      

構造器會幫我們申請記憶體空間後,然後把這個對象隐式地傳回來,再用構造器方法對其進行指派。不要在構造器中顯示return傳回其他東西,否則将會取代之前申請好的對象。前面的功夫就白做了,如:

var MyClass = function(name){
      this.name = name;
      return {
        name:"Jeety"
      }
    }
var myClass = new MyClass("Tome");
alert(myClass.name) // Jeety      

上面這個反例裡做事就是一開始去申請了記憶體空間,然後用構造器函數對記憶體空間進行了指派,讓記憶體有了一個name值為Tome,結果在最後又去建立了一個字面量對象,在記憶體另開一個空間,把name指派了Jeety,最後把前面那個抛棄了,傳回這個字面量對象。不要這樣做, 我真想說,這是吃飽了撐的嗎?

4、Function.prototype.call或Function.prototype.apply調用

.id = "window";
      document.getElementById("root").onclick = function(){
        alert(this.id); // root
        var callback = function(){
          alert(this.id);
        };
        callback();
        callback.call(document.getElementById("root")); // root
        callback.apply(document.getElementById("root")); // root
      }