this理解

this对象是在运行时基于函数的执行环境的定的。

用法

上下文

无论是否在严格模式下,在全局执行上下文中(在任何函数体外部)this 都指代全局对象。

1
2
3
4
5
6
7
8
// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b) // "MDN"

在全局上下文下调用函数,函数中的this,在严格模式下为全局对象,在非严格模式下为undefined。

1
2
3
4
5
6
7
8
function f1(){
return this;
}
//在浏览器中:
f1() === window; //在浏览器中,全局对象是window
//在Node中:
f1() === global;

函数作为对象的一个属性

如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。

1
2
3
4
5
6
7
8
9
var name = "The Window";
var object = {
name: "My Object",
getName: function() {
return this.name;
}
}
object.getName() // "My Object"

匿名函数的this略有不同,通常指向window。

1
2
3
4
5
6
7
8
9
10
11
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
}
}
}
object.getNameFunc()(); // "The Window"

匿名函数的在搜索活动对象this和arguments的时候会在作用域链上搜索,匿名函数外的执行环境为getNameFunc的执行环境getNameFunc的执行环境中的活动对象并没有this.name,就会停止搜索,直接将this指向window。

解决这种问题的方式是把this保存作为活动对象。

1
2
3
4
5
6
7
8
9
10
11
12
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return this.name;
}
}
}
object.getNameFunc()(); // "My Object"

函数用call或者apply调用

如果要想把 this 的值从一个上下文传到另一个,就要用 call 或者apply 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 一个对象可以作为call和apply的第一个参数,并且this会被绑定到这个对象。
var obj = {a: 'Custom'};
// 这个属性是在global对象定义的。
var a = 'Global';
function whatsThis(arg) {
return this.a; // this的值取决于函数的调用方式
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'

作为构造函数

通过new Foo()一个对象的方式调用构造函数,那么在函数中访问this的时候,this指向的是新对象。

1
2
3
4
5
6
7
8
function Foo() {
this.name = 'lucky4';
this.year = 1993;
console.log(this); // Foo { name: "lucky4", year: 1993 }
}
var f1 = new Foo();

this指向新创造对象的原因是,在使用new操作符后,调用构造函数会经历一下4个步骤:

  1. 创建一个新对象。
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
  3. 执行构造函数中的代码。
  4. 返回新对象。
1
2
3
4
5
6
7
8
function Foo() {
this.name = 'lucky4';
this.year = 1993;
console.log(this); // Window {...}
}
Foo();

如果未通过new的方式访问构造函数,那么this将指向Window。

arrow function 中的this

在箭头函数中,this与封闭词法上下文的this保持一致。在全局代码中,它将被设置为全局对象。

1
2
3
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 创建一个含有bar方法的obj对象,
// bar返回一个函数,
// 这个函数返回this,
// 这个返回的函数是以箭头函数创建的,
// 所以它的this被永久绑定到了它外层函数的this。
// bar的值可以在调用中设置,这反过来又设置了返回函数的值。
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
// 作为obj对象的一个方法来调用bar,把它的this绑定到obj。
// 将返回的函数的引用赋值给fn。
var fn = obj.bar();
// 直接调用fn而不设置this,
// 通常(即不使用箭头函数的情况)默认为全局对象
// 若在严格模式则为undefined
console.log(fn() === obj); // true
// 但是注意,如果你只是引用obj的方法,
// 而没有调用它
var fn2 = obj.bar;
// 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。
console.log(fn2()() == window); // true

JavaScript中的this与Java、C++中的this有什么区别?

首先看一段Java的代码:

1
2
3
4
5
6
7
8
9
10
class Boolean { void peel(int i) { /* ... */ } }
public class BananaPeel {
public static void main(String[], args) {
Banana a = new Banana(),
b = new Banana();
a.peel(1);
b.peel(2);
}
}

在执行代码的时候,编译器做了一些幕后工作,它把所操作对象的应用作为第一个参数传递给了peel(),上面的两个方法的调用变成了这样:

1
2
Banana.peel(a, 1);
Banana.peel(b, 2);

假设你希望在方法内部获得对当前对象的引用。由于上面引用是由编译器传入的,所以没有表示符可用,为此有个专门的关键字:this。this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。

Java中this的用处有很多包括,返回对当前对象的引用,方便链式调用;将当前对象传递给其他方法;在一个构造器中调用另一个构造器等。

我认为Java的this和JavaScript的this主要有以下几个区别:

  1. this的值,Java中的this是一个引用,JavaScript中的this是一个类数组对象。
  2. Java中的this创建后不可改变,JavaScript中的this可以通过使用call,apply,bind等方式改变。也就是回到了对定义的理解Java中的this是编译时绑定的,而JavaScript中的this是基于运行时绑定的。

参考:
http://www.cnblogs.com/wangfupeng1988/p/3988422.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
《JavaScript高级程序设计》
《Java编程思想》