JavaScript 是一门面向对象的语言。
尽管如此, JavaScript
中的对象与其它语言的实现也不尽相同。
在 JavaScript
中,函数也是对象,它们是 Function
对象。
说到对象,就要说到关键字 this
了。
在 JavaScript
中,关键字 this
与其它面向对象语言中的 this
稍微有些不同。
有以下几点规则:
this
仅能在函数内部使用。它代表函数运行时,自动生成的一个内部对象;- 大部分情况下,
this
的值取决于这个函数是如何被调用的;- 因此,在函数调用的不同时间里,
this
有可能是不同的;- 此外,在
strict mode
和non-strict mode
,this
的行为还有点不同。(本文不涉及)
我们可以从以下几点来了解 this
的用法。
Global context¶
在全局执行上下文中(也即不在任何函数内), this
指向全局对象。
不管是否在 strict mode
还是 non-strict mode
.
1 2 3 4 5 6 7 8 |
// Don't use var to state this variable.
X = 2000;
function foo() {
console.log(this.X);
}
foo(); // 200
|
尝试着修改全局变量:
1 2 3 4 5 6 7 8 9 |
X = 2000;
function foo() {
this.X = 3000;
}
foo();
console.log(X); // 300
|
Function context¶
这是 this
最为常见的应用场景。对于 Java
而言, this
常出现在类方法中,
而 JavaScript
中函数也是一种特殊的类。
Tip
再强调一次:在一个函数中, this
的值取决于函数是如何被调用的。
Simple call¶
1 2 3 4 5 |
function foo() {
console.log(this);
}
foo();
|
this
指向了全局空间。foo
函数的调用者属于全局执行环境。this
也就指向了全局空间。As an object method¶
1 2 3 4 5 6 7 8 |
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37
|
当函数是作为对象的一个方法被调用时, this
指向了对象 o
.
还需注意的是,此时 this
的绑定行为与函数是如何被定义是 无关 的。
1 2 3 4 5 6 7 8 9 |
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // 37
|
this
还是指向对象 o
.
同样的, this
通常与离它最近的成员引用绑定在一起。
1 2 3 4 5 6 7 8 9 10 11 12 |
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // logs 42
|
As a constructor¶
当函数是被当作 constructor
来调用(使用 new
关键字)时, this
被绑定到新分配的对象上。
Note
尽管 constructor
默认会返回由 this
指向的对象,我们可以使它返回一些其它的对象(如果返回的值不是一个对象,那么 this
对象还是会被返回)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2(){
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); // logs 38
|
call
and apply
¶
所有对象都有从 Function.prototype
继承而来的 call
和 apply
方法。
当函数在函数体内使用 this
关键字时,通过调用 call
和 apply
方法, this
能够被绑定到特定的对象。
注意,如果传递给 call
或 apply
的 this
并不是一个对象, JavaScript
会尝试使用它内部的 ToObject
方法将它转换为一个对象。
the bind
method¶
尽管调用 f.bind(someObject)
将会创建一个与 f
有着相同内容?和作用域的新函数,但 this
发生在原始的函数中。
所以在新的函数中,它被绑定到 bind
的第一个参数而不管该函数是被如何调用的。
1 2 3 4 5 6 7 8 9 |
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
|
As a DOM event handler¶
当一个函数是以事件处理方法(event handler)调用时, this
指向触发该事件的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// When called as a listener, turns the related element blue
function bluify(e){
// Always true
console.log(this === e.currentTarget);
// true when currentTarget and target are the same object
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
// Get a list of every element in the document
var elements = document.getElementsByTagName('*');
// Add bluify as a click listener so when the
// element is clicked on, it turns blue
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
|
In an in-line event handler¶
1 2 3 |
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
|
1 2 3 |
<button onclick="alert((function(){return this}()));">
Show inner this
</button>
|
当代码是从一个内联(in-line)的处理函数中执行时, this
指向被监听的DOM元素。
总结¶
尽管 this 有如此多的规则。我们可以将它总结成以下两条:
Important
this
的值取决于这个函数是如何被调用的this
通常与离它最近的成员引用绑定在一起
但凡遇到困惑的时候,仔细想想这两条规则,能够帮助你理清 this
真正指向的对象。