Javascript:this用法

#Javascript:this用法整理
pingan 于 星期三, 18/12/2013 - 22:32 提交

常用Javascript的人都知道,[this这个关键字在一个函式内究竟指向谁]的这个问题很令人头大,本人在这裡整理了一下Javascript中this的指向的五种不同情况,其中前三种属于基本的情况,而后两种情况可基于前三种情况的方式来进行思考。

1.this指向于调用该函式之物件
如果你有学过C/C++,你可能会记得一个物件内的成员函式裡的this指的即是该成员函式所在之物件,但在Javascript裡则有那麽些许不同,Javascript裡的this看的是究竟是谁调用该函式,而不是看该函式被定义在哪个物件内,这个大原则抓到了,基本上就已经可以探知this的奥秘了。底下写一下这种情况的公式与范例:

公式

 1 物件.函式(); //函式内的this指向该物件
范例

var obj = {
  x: 20,
  f: function(){ console.log(this.x); }
};

obj.f(); //由于调用f函式时,点前面物件为obj,故f内的this指向obj,则输出为20。

obj.innerobj = {
  x: 30,
  f: function(){ console.log(this.x); }
}

obj.innerobj.f(); //由于调用f函式时,点前面物件为obj.innerobj,故f内的this指向obj.innerobj,则输出为30。

2.this指向全域物件(浏览器:window物件、node.js:GLOBAL物件)
如果调用函式的前方并未有物件,则函式内this就指向全域物件。在浏览器内全域物件为window物件,而在node.js中全域物件则为GLOBAL物件。底下一样写一下这种情况的公式与范例:

公式


 函式(); //函式内的this指向全域物件
范例

var x = 10;
var f = function(){
  console.log(this.x);
};

f(); //由于调用f函式时,前方并未有[物件.]的形式,故f内的this指向全域物件,则输出全域变数的x(10)。
例外:在使用node.js时,若使用node file.js这样的方式执行js档,并不会让宣告的全域变数挂在全域物件上(意指会利用function将code整个包起来执行),故输出应为undefined。
前两种情况常见误导范例
范例一、物件之成员函式内有函式(感谢NSLin在实务读书会上的范例Code)
example1.js


var x = 10;
var obj = {
  x: 20,
  f: function(){
    console.log(this.x);
    var foo = function(){ console.log(this.x); }
    foo(); // (2)
  }
};

obj.f(); // (1)
这个范例会输出多少呢?别忘记大原则,在Javascript裡的this看的是究竟是谁调用该函式,故并不会输出20 20,而是输出20 10,为什麽呢?因为(1)obj.f()调用时,f前面物件为obj,故f内的this指向obj。但因为调用f内的(2)foo函式时是用foo(),调用的前方并未有物件,故foo内的this指向全域物件,所以输出会是全域变数的x的值。

若要让foo内使用obj.x的值,解法如下:

example1.js


var x = 10;
var obj = {
  x: 20,
  f: function(){
    console.log(this.x);
    var that = this; //使用that保留在这个函式内的this
    var foo = function(){ console.log(that.x); } //使用that取得obj
    foo();
  }
};

obj.f();
范例二、借用函式
example2.js


var x = 10;
var obj = {
  x: 20,
  f: function(){ console.log(this.x); }
};

obj.f(); // (1)

var fOut = obj.f;
fOut(); //(2)

var obj2 = {
  x: 30,
  f: obj.f
}

obj2.f(); // (3)
范例中三次调用之函式的this所指向的物件为何,不知道各位能不能看得出来。虽然用的是同一个函式,但是因为调用的不同,故this所指向的物件就不同。(1)obj.f()的f所指向的是obj,这比较没有问题,输出的会是20;而(2)fOut()裡的this,则是因为调用时前方无物件,则this所指的是全域物件,输出的会是10;最后(3)obj2.f()则是obj2去呼叫f,故f内的this指向的是obj2,输出的会是30。

3.this指向利用call或apply所指派给this的物件
有个方法可以更改前两种叙述中this指派的值,就是利用call与apply。call与apply都是呼叫该函式并让该函式的this指向给予call或apply的第一个参数。至于call和apply的差别则是在于其后面给予被调用之函式的参数放入的方法不同,一个是直接摊平放在第二个以后的参数;一个是直接放入一个裡面放要给予之参数的阵列。底下一样看一下公式和范例:

公式

(A物件.)函式.call(B物件,参数1,参数2,参数3, ......); //函式的this指向B物件(若B物件为null,则指向全域物件)
(A物件.)函式.apply(B物件,[参数1,参数2,参数3, ......]); //函式的this指向B物件(若B物件为null,则指向全域物件)
范例

var obj = {
  x: 20;
  f: function(){ console.log(this.x); }
};

var obj2 = {
  x: 30;
};

obj1.f.call(obj2); //利用call指派f的this为指向obj2,故输出为30
4.this指向new所产生之新物件
若将函式当作建构式(constructor)来用,则内部的this则指向于new所产生之新物件。

公式

new 建构式(); //建构式内之this指向new所产生之新物件
范例

function Monster(){
  this.hp = 100;
};

var monster = new Monster(); //Monster的this指向new出来之新物件并回传回来,new的写法就类似于下面的写法。
var monster = (function(){
  var _new = {

    constructor: Monster,

     __proto__: Monster.prototype

  }; //在IE内可能不相似
  _new.constructor(); //这也是为何说可以利用前三种情况来变化的原因,constructor呼叫时,this指向的即是_new这个物件。
  return _new;
})();
5.callback函式内的this会指向于调用放入该callback的函式之this所指向之物件
先想想在jQuery中,我们若要让#button这个元素被click的时候,内容改为“Clicked”这样的字串,该如何写呢?

clicked.js


$('#button').click(function(){
  this.html("Clicked");
})
此时这个this居然会指向$(‘#button’)这个物件,感觉很自然,但实际想想会觉得很神奇。假设你写一个function,它可以传入一个function,并在里面呼叫传入的function,你该怎么写呢?

function-to-function.js


var f = function(innerf){
  //前面的处理
  innerf(arg1, arg2, arg3, ......);
  //后面的处理
}
但如果这样写的话,innerf里的this根据前述规则就应该是全域物件了!那为什么常常别人实作的callback函式可让this指向于调用放入该callback的函式之this所指向之物件呢?这表示大家实际上会遵守一个规则,会将自己的this传给callback当作它的this来用!这也是为什麽我说这个情况其实也是前三种情况的变化而已了!所以上面的code应该改成如下的形式会比较好:

function-to-function-improved.js


var f = function(innerf){
  //前面的处理
  innerf.call(this, arg1, arg2, arg3, ......);
  //或是innerf.apply(this, [arg1, arg2, arg3, ......])
  //后面的处理
}

posted @ 2013-12-18 22:44  悠悠子瞻  阅读(466)  评论(0编辑  收藏  举报