this之认识

在 JavaScript 中,this 是当前执行函数的上下文。因为 JavaScript 有4种不同的函数调用方式:

函数调用: alert(‘Hello World!’)

方法调用: console.log(‘Hello World!’)

构造函数调用: new RegExp(‘\d’)

隐式调用: alert.call(undefined, ‘Hello World!’)

函数调用指执行构成一个函数的代码(简单说就是 call 一个函数)例如 parseInt(‘15’)是 parseInt 函数调用.

函数调用的上下文指 this 在函数体中的值。 即 this指的是调用函数的那个对象,例:object.fn(),这里的 this 便是 object。

函数的作用域指的是在函数体内可以使用的变量、对象以及函数的集合。

函数调用


1
2
3
4
5
6
function hello(name) {
return 'Hello ' + name + '!';
}
// Function invocation
var message = hello('World'); console.log(message);
// => 'Hello World!'

更加高级的例子是 IIFE (立即调用的函数表达式):

1
2
3
4
var message = (function(name) {
return 'Hello ' + name + '!';
})('World');
console.log(message) // => 'Hello World!'

IIFE 也是一个函数调用: 第一对括号(function(name) {…}) 是一个等价于函数的表达式, 紧接着一对括号以及’World’参数: (‘World’)

函数调用中的this

this 在函数调用中是一个全局对象

全局对象是由执行的环境决定的。在浏览器里它是window对象。

在函数调用里,函数执行的上下文是全局对象。让我们一起看看下面函数里的上下文:

1
2
3
4
5
6
7
8
9
10
function sum(a, b) {
console.log(this === window); // => true
this.myNumber = 20;
// add 'myNumber' property to global object
return a + b;
}
// sum() is invoked as a function
// this in sum() is a global object (window)
sum(15, 16); // => 31
window.myNumber; // => 20

函数调用中的 this, strict 模式


strict 模式下,函数调用中的 this 是 undefined

为了使用它,把’use strict’放在函数体的开始。这个模式会影响执行的上下文,把 this 变成 undefined。函数执行的上下文跟上面的例子相反,不再是全局对象

在strict模式下执行函数的例子:

1
2
3
4
5
6
7
function multiply(a, b) {
'use strict'; // enable the strict mode
console.log(this === undefined); // => true
return a * b;
}
// multiply() function invocation with strict mode enabled // this in multiply() is undefined
multiply(2, 5); // => 10

当 multiply(2, 5) 作为函数被调用时,this 是 undefined。

strict 模式不仅在当前作用域起作用,也会对内部的作用域起作用(对所有在内部定义的函数有效):

1
2
3
4
5
6
7
8
9
10
11
12
function execute() {
'use strict'; // activate the strict mode
function concat(str1, str2) {
// the strict mode is enabled too
console.log(this === undefined); // => true
return str1 + str2;
}
// concat() is invoked as a function in strict mode
// this in concat() is undefined
concat('Hello', ' World!'); // => "Hello World!"
}
execute();

‘use strict’ 插入在 execute 函数体的一开始, 使它在 execute 函数的作用域内起作用。 因为 concat 定义在 execute 的作用域内, 它也会继承 strict 模式,

这导致调用 concat(‘Hello’, ‘ World!’)时, this 是 undefined。

单个的JavaScript文件可能既包含 strict 模式又包含 非strict 模式。所以,在单个的脚本内,同样的调用方法可能有不同的上下文行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function nonStrictSum(a, b) {
// non-strict mode
console.log(this === window); // => true
return a + b;
}
function strictSum(a, b) {
'use strict'; // strict mode is enabled
console.log(this === undefined); // => true
return a + b;
}
// nonStrictSum() is invoked as a function in non-strict mode
// this in nonStrictSum() is the window object
nonStrictSum(5, 6); // => 11
// strictSum() is invoked as a function in strict mode
// this in strictSum() is undefined
strictSum(8, 12); // => 20

陷阱: 内部函数中的this


一个函数调用中的常见错误就是以为 this 在内部函数中跟在外部函数中一样。 正确来说,内部函数的上下文依赖于调用方法,而不是外部函数的上下文。

为了能使 this 跟预期的一样,用隐式调用来修改内部函数的上下文 (用.call()或者.apply()) 或者创建一个绑定函数 (用.bind())

下面的例子计算了2个数字的和:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
// this is window or undefined in strict mode
console.log(this === numbers); // => false
return this.numberA + this.numberB;
}
return calculate();
}
};
numbers.sum();
// => NaN or throws TypeError in strict mode

numbers.sum() 是一个对象上的方法调用 ,所以 sum 中的上下文是 numbers 对象。calculate 函数定义在 sum 内部,所以你会指望 calculate()

中的 this 也是 numbers 对象。然而,calculate() 是一个函数调用(而不是方法

调用),它的 this 是全局对象 window 或者 strict 模式下的 undefined。即使外部函数 sum 的上下文是 numbers对象,它在这里也没有影响。numbers.sum()的调用结果是NaN或者

strict 模式下的 TypeError: Cannot read property ‘numberA’ of undefined 错误。因为 calculate 没有被正确调用,结果绝不是预期的 5 + 10 = 15。

为了解决这个问题,calculate应该跟sum有一样的上下文,以便于使用numberA和numberB。解决方法之一是使用.call()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
console.log(this === numbers); // => true
return this.numberA + this.numberB;
} // use .call() method to modify the context
return calculate.call(this);
}
};
numbers.sum(); // => 15

calculate.call(this) 像往常一样执行 calculate,但是上下文由第一个参数指定。现在 this.numberA + this.numberB 相当于 numbers.numberA + numbers.numberB,函数会返回预期的结果 5 + 10 = 15。

深入了解更多参阅:http://www.w3cplus.com/javascript/gentle-explanation-of-this-in-javascript.html