在javascritp中,不一定只有對象方法的上下文中才有this, 全局函數調用和其他的幾種不同的上下文中也有this指代。
它可以是全局對象、當前對象或者任意對象,這完全取決於函數的調用方式。JavaScript 中函數的調用有以下幾種方式:作為對象方法調用,作為函數調用,作為構造函數調用,和使用 apply 或 call 調用。
1.作為對象方法調用
在 JavaScript 中,函數也是對象,因此函數可以作為一個對象的屬性,此時該函數被稱為該對象的方法,在使用這種調用方式時,this 被自然綁定到該對象。
復制代碼 代碼如下:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
}
};
point.moveTo(1, 1)//this 綁定到當前對象,即 point 對象
2.作為函數調用
函數也可以直接被調用,此時 this 綁定到全局對象。在浏覽器中,window 就是該全局對象。比如下面的例子:函數被調用時,this 被綁定到全局對象,接下來執行賦值語句,相當於隱式的聲明了一個全局變量,這顯然不是調用者希望的。
復制代碼 代碼如下:
function makeNoSense(x) {
this.x = x;
}
makeNoSense(5);
x;// x 已經成為一個值為 5 的全局變量
對於內部函數,即聲明在另外一個函數體內的函數,這種綁定到全局對象的方式會產生另外一個問題。我們仍然以前面提到的 point 對象為例,這次我們希望在 moveTo 方法內定義兩個函數,分別將 x,y 坐標進行平移。結果可能出乎大家意料,不僅 point 對象沒有移動,反而多出兩個全局變量 x,y。
復制代碼 代碼如下:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
// 內部函數
var moveX = function(x) {
this.x = x;//this 綁定到了哪裡?
};
// 內部函數
var moveY = function(y) {
this.y = y;//this 綁定到了哪裡?
};
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
point.x; //==>0
point.y; //==>0
x; //==>1
y; //==>1
這屬於 JavaScript 的設計缺陷,正確的設計方式是內部函數的 this 應該綁定到其外層函數對應的對象上,為了規避這一設計缺陷,聰明的 JavaScript 程序員想出了變量替代的方法,約定俗成,該變量一般被命名為 that。
復制代碼 代碼如下:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
var that = this;
// 內部函數
var moveX = function(x) {
that.x = x;
};
// 內部函數
var moveY = function(y) {
that.y = y;
}
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
point.x; //==>1
point.y; //==>1
作為構造函數調用
JavaScript 支持面向對象式編程,與主流的面向對象式編程語言不同,JavaScript 並沒有類(class)的概念,而是使用基於原型(prototype)的繼承方式。相應的,JavaScript 中的構造函數也很特殊,如果不使用 new 調用,則和普通函數一樣。作為又一項約定俗成的准則,構造函數以大寫字母開頭,提醒調用者使用正確的方式調用。如果調用正確,this 綁定到新創建的對象上。
復制代碼 代碼如下:
function Point(x, y){
this.x = x;
this.y = y;
}
使用 apply 或 call 調用
讓我們再一次重申,在 JavaScript 中函數也是對象,對象則有方法,apply 和 call 就是函數對象的方法。這兩個方法異常強大,他們允許切換函數執行的上下文環境(context),即 this 綁定的對象。很多 JavaScript 中的技巧以及類庫都用到了該方法。讓我們看一個具體的例子:
復制代碼 代碼如下:
function Point(x, y){
this.x = x;
this.y = y;
this.moveTo = function(x, y){
this.x = x;
this.y = y;
}
}
var p1 = new Point(0, 0);
var p2 = {x: 0, y: 0};
p1.moveTo(1, 1);
p1.moveTo.apply(p2, [10, 10]);
在上面的例子中,我們使用構造函數生成了一個對象 p1,該對象同時具有 moveTo 方法;使用對象字面量創建了另一個對象 p2,我們看到使用 apply 可以將 p1 的方法應用到 p2 上,這時候 this 也被綁定到對象 p2 上。另一個方法 call 也具備同樣功能,不同的是最後的參數不是作為一個數組統一傳入,而是分開傳入的。
復制代碼 代碼如下:
function Foo(){
//1.this引用的構造函數是argument.callee引用的對象
//說明是通過new操作符執行的構造函數
if(this.constructor==arguments.callee){
alert('Object Created');
}
//2.this是window, 那麼是全局調用
if(this==window){
alert('normal call');
}
else{//3.否則是作為其他對象的方法來調用
alert('called by '+ this.constructor);
}
}
Foo();//全局函數調用
Foo.call(new Object());//作為一個object對象的成員方法來調用
new Foo();//被new操作符調用,執行對象構造