22this关键字
概述
this是什么
this文键字是Javascript中最复杂的机制之ー。它是一个很特别的关键字,被自动定义在所有函数的作用域中。但是即使是非常有经验的 Javascript开发者也很难说清它到底指向什么。
实际上, Javascript中this的机制井没有那么先进,但是开发者往往会把理解过程复杂化。亳不夸张地说,不理解它的含义,大部分开发任务都无法完成。
this都有一个共同点,它总是返回一个对象。简单说,this就是属性或方法“当前”所在的对象。
为什么使用this
this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁井且易于复用。
随着使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用this则不会这样。
调用位置
想要了解this的绑定过程,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的位置)
通常来说,寻找调用位置就是寻找“函数被调用的位置”。最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数
函数调用
1
2
3
4
5
6
7
8
9
10var v = 100;
// this 经常被定义在函数的作用域中
function fn() {
// this总是要返回一个对象
console.log(this.v); //this指向哪个对象,不取决于定义的位置
}
// this 指向哪个对象,取决于调用的位置
fn(); // 函数的调用 结果为:100(浏览器环境)对象调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var v = 100;
// this 经常被定义在函数的作用域中
function fn() {
// this总是要返回一个对象
console.log(this.v); //this指向哪个对象,不取决于定义的位置
}
// 定义一个对象,将fn函数作为obj对象的方法
var obj = {
v: 200,
f: fn,
};
obj.f(); //200直接调用
1
2
3var v = 100;
// this 指向哪个对象,取决于调用的位置
console.log(this.v); // 100 (浏览器环境)
绑定规则
默认绑定
在一个函数体中使用this,当该函数被独立调用。可以把这条规则看作是无法应用其他规则时的默认规则。
1 | function fn() { |
声明在全局作用域中的变量(比如var v=100
)就是全局对象的一个同名属性。当调用fn()
函数时,this.v
被解析成了全局变量v
。
函数调用时应用了this的默认绑定,因此this指向全局对象。
隐式绑定
隐式绑定的规则需要考虑的是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。当然,这种说法井不准确。
1 | function fn() { |
调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象“拥有”或者“包含”它。
隐式丢失
隐式丢失是最常见的this绑定问题,指的就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象。
1 | // 定义一个全局变量 |
显式绑定
显式绑定就是明确在调用时,this所绑定的对象。 Javascript中提供了apply()
方法和call()
方法实现,这两个方法的第一个参数接收是一个对象,会把这个对象绑定到this,接着在调用函数时指定这个this。
1 | // 定义一个全局变量 |
new绑定
在Javascript中,构造函数只是一些使用new操作符时被调用的函数。包括内置对象函数在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。
使用new来调用函数,会自动执行下面的操作:
- 创建(或者说构造)一个全新的对象。
- 这个新对象会绑定到函数调用的this.
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
1 | function Hero(name) { |
示例
1 | function Hero() { |
绑定例外
被忽略的this
如果把null或者undefined作为this的绑定对象传入call, applya或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
情况1
1
2
3
4
5
6function fn() {
console.log(this.v);
}
var v = 100;
fn.call(null); // node环境下为undefined 浏览器环境下为100情况2
1
2var result = Math.max.apply(null, [1, 2, 3, 4, 5, 6]);
console.log(result); // 6
间接引用
有可能(有意或者无意地)创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则
1 | function foo() { |
注意事项
避免多层this
情况1
1
2
3
4
5
6
7
8
9
10
11
12function fn() {
console.log(this.a);
function n() {
// this指向全局,因为独立调用
console.log(this.a);
}
n();
}
fn();
// undefined
// undefined情况2
1
2
3
4
5
6
7
8
9
10
11
12var obj = {
v: 100,
fn: function () {
console.log(this.v);
function n() {
// this指向全局
console.log(this.v);
}
n();
},
};
obj.fn();
避免数组方法中的this
情况1
1
2
3
4
5
6var arr = [1, 2, 3, 4, 5, 6];
arr.forEach(function (value, index) {
// this指向全局
console.log(this.v);
});情况2
1
2
3
4
5
6
7
8
9
10
11
12var obj = {
v: 100,
arr: [1, 2, 3, 4, 5],
f: function () {
this.arr.forEach(function (value) {
// this指向全局
console.log(this.v);
});
},
};
obj.f();
避免回调函数中的this
1 | var obj = { |