JavaScript中this关键字详解

引言

  • js中,this关键是令人头疼的问题,它的指代对象和this所处的上下文环境、函数调用者等多重因素有关;
  • 我们要解决的问题是,this关键字代指哪个对象;
  • 本文用丰富的实例让您清晰、明确的判断各种情况下,this关键字所指代的对象;

判断原则:

  • 原则一:this指的是直接调用函数的那个对象,没有的话就值window;
  • 原则二:this代指的对象就是它使用的时候(不是定义的时候)所处的环境的上下文;
  • 原则三:当new关键字出现时,this指代new出来的那个对象;
  • this是js关键字,某些程序中出现的that仅仅是普通的变量,不是关键字;
  • 下面,我们用实例说话,一一验证上述原则;

无对象调用函数时this指代window的例子

  • 例一:
1
2
3
4
5
6
function test(){
this.x = 1;
alert(this.x);
}
test(); //1
alert(x); //1
  • 例二:
1
2
3
4
5
var x = 1;
function test(){
alert(this.x);
}
test(); //1
  • 例三:
1
2
3
4
5
6
var x = 1;
function test(){
this.x = 0;
}
test();
alert(x); //0
  • 例四:
1
2
3
4
5
6
7
8
9
var x = 0;
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); //0 apply参数为空时,默认调用全局对象,故this指向window
o.m.apply(o); //1 test函数的调用者是o,故this指代对象o
  • 例五:
    • 执行第一个this时,function调用者为obj,故this指代obj对象;
    • 执行f函数和匿名函数时,函数没有直接的调用者,故this指代window;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var context = "global";

var obj = {
context: "object",
method: function () {
console.log(this + ":" +this.context); // [object Object]:object

function f() {
var context = "function";
console.log(this + ":" +this.context); // [object Window]:global
};
f();

(function(){
var context = "function";
console.log(this + ":" +this.context); // [object Window]:global
})();
}
};

obj.method();

有对象调用函数时this指代调用函数者的例子

  • 例一:
    • o调用了test函数,故this指代对象o
1
2
3
4
5
6
7
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); //1
  • 例二:
    • getInfo()函数的调用者是ext1,this指代ext1;

this1

  • 例三:
    • getInfo()函数的调用者是ext1,this指代ext1;
    • this.id和this.name都是代指ext1对象的id和name,但是由于ext1对象本身没有name属性,所以需要到原型链上查找name属性;

this2

  • 例四:
    • 内层的funcion中包含了this,但是,内层function没有直接调用者;
    • 故这里的this指window;

this3

  • 例五:
    • object对象调用了外层函数,所以var that = this中的this指代object对象,执行完此句后,that也指代object对象;
    • that并不是js的关键字,仅仅是一个普通的变量,所以下面return that.name的时候,返回的是object.name;

this4

  • 例六:
    • object对象直接调用了外层的function,故var that = this中的this指代object对象,执行完此句后,that也指代object对象;
    • 内层function没有直接调用者,故内层函数中的this指代window;

this5

  • 例七:
    • point对象调用moveTo方法,故this指代point对象;
1
2
3
4
5
6
7
8
9
10
11
12
var point = { 
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
}
};
point.moveTo(1,1);
console.log(point.x); //1
console.log(point.y); //1
console.log(x); //x is not defined
  • 例八:
    • moveX和moveY函数没有调用者,所以其中的this指代window,而不是point对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var point = { 
x : 0,
y : 0,
moveTo : function(x, y) {
var moveX = function(x) {
this.x = x;
};

var moveY = function(y) {
this.y = y;
};

moveX(x);
moveY(y);
}
};
point.moveTo(1,1);
point.x; //=>0
point.y; //=>0
x; //=>1
y; //=>1

this指代new出来的那个对象

  • 例一:
    • 由于存在new关键字,this指代的对象是new出来的对象,即对象o;
1
2
3
4
5
function test(){
this.x = 1;
}
var o = new test();
alert(o.x); //1
  • 例二:
    • this指代对象o,而不是window;
1
2
3
4
5
6
var x = 2;
function test(){
this.x = 1;
}
var o = new test();
alert(x); //2
  • 例三:
    • np对象是new出来的,所以this指代np对象;
    • p对象不是new出来的,且Point函数无调用者,故this指代window;
1
2
3
4
5
6
7
8
9
function Point(x,y){ 
this.x = x;
this.y = y;
}
var np=new Point(1,1);
np.x; //1
var p=Point(2,2);
p.x; //error, p是一个空对象undefined
window.x; //2
  • 例四:
    • apply使得Point函数的调用者为p2,故this指代p2对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
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.apply(p2, [10, 10]); //apply实际上为p2.moveTo(10,10)
p2.x; //10

bind/call/apply重定向this关键字指向

  • 例一:
    • apply和call将函数的调用者转变了,所以this指代的对象随之转变;
    • bind创建一个新的函数,这个函数中的this对象指代的内容也由bind函数指定;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add(numA, numB){
console.log( this.original + numA + numB);
}

add(1, 2); // NaN

var obj = {original: 10};
add.apply(obj, [1, 2]); // 13
add.call(obj, 1, 2); // 13

var f1 = add.bind(obj);
f1(2, 3); // 15

var f2 = add.bind(obj, 2);
f2(3); // 15

js事件中的this指代

  • 例一:
    • 前面的一个,console调用者是button对象,故this指代button对象;
    • 中间的一个,匿名函数没有直接调用者,所以this指代window;
    • 第三个,通过bind重定向函数中的this指向,所以this代表button对象;
1
2
3
4
5
6
7
8
document.write('<button onclick="console.log(this)">Show this</button>');
// <button onclick="console.log(this)">Show this</button>

document.write('<button onclick="(function(){console.log(this);})()">Show this</button>');
// window

document.write('<button onclick="((function(){console.log(this);}).bind(this))()">Show this</button>');
// <button onclick="((function(){console.log(this);}).bind(this))()">Show this</button>
  • 其他的js事件中的this关键字指代请参考这里

this关键字指代对象的暂存方法

  • 说明:
    • 有些时候希望在this指代正确的对象的时候将this指代的对象暂存起来,备用;
  • 例一:
    • 下面程序将this指代的bar对象暂存到self对象中;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var bar = {
name: "bar",
body: document.getElementsByTagName("body")[0],

greeting: function(){
console.log("Hi there, I'm " + this + ":" + this.name);
},

anotherMethod: function () {
var self = this;
this.body.addEventListener("click", function(){
self.greeting();
});
}
};

bar.anotherMethod(); // Hi there, I'm [object Object]:bar
  • 上面的程序也可以用bind实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var bar = {
name: "bar",
body: document.getElementsByTagName("body")[0],

greeting: function(){
console.log("Hi there, I'm " + this + ":" + this.name);
},

anotherMethod: function () {
this.body.addEventListener("click", (function(){
this.greeting();
}).bind(this));
}
};

bar.anotherMethod(); // Hi there, I'm [object Object]:bar
您的支持是对我最大的鼓励!