Monthly Archives: 十二月 2014

JavaScript中this的用法

这篇文章写的

JavaScript 是一门面向对象的语言。
尽管如此, JavaScript 中的对象与其它语言的实现也不尽相同。

JavaScript 中,函数也是对象,它们是 Function 对象。

说到对象,就要说到关键字 this 了。

JavaScript 中,关键字 this 与其它面向对象语言中的 this 稍微有些不同。

有以下几点规则:

  1. this 仅能在函数内部使用。它代表函数运行时,自动生成的一个内部对象;
  2. 大部分情况下, this 的值取决于这个函数是如何被调用的;
  3. 因此,在函数调用的不同时间里, this 有可能是不同的;
  4. 此外,在 strict modenon-strict modethis 的行为还有点不同。(本文不涉及)

我们可以从以下几点来了解 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 继承而来的 callapply 方法。
当函数在函数体内使用 this 关键字时,通过调用 callapply 方法, this 能够被绑定到特定的对象。
注意,如果传递给 callapplythis 并不是一个对象, 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 真正指向的对象。

参考文献