对于许多JavaScript的初学者来说,this是一个让人又爱又恨的关键词,它的指向灵活多变,稍有不慎就会出现意想不到的结果。但是掌握this的绑定规则却是写出优雅,可复用的代码的必经之路。所以,本文将带你系统梳理this的来龙去脉,彻底搞懂它的工作机制。
一.为什么要有this
this 是 JS 中的一个关键字,它提供了一种更优雅的方式隐式的传递一个对象的引用,可以让代码更简洁易于复用。
// 优雅的 this
const alice = {
name: 'Alice',
age: 25,
introduce() {
console.log(this.name);
}
};
alice.introduce(); // this隐式传递了 alice 对象
二. this 可以出现在哪里?
1.全局
在浏览器中,全局中 this 指向 window 对象。
console.log( this === window);// true
2.函数体内
函数内部的 this 指向取决于如何调用,而不是在哪里定义,这也是 this 最复杂也是最灵活的地方。
三.this 的绑定规则
1.默认绑定
当函数独立调用时,函数中的 this 指向 window 对象
function foo(){
console.log(this);
}
foo(); // window
2.隐式绑定
当一个函数被一个上下文对象所拥有,并被该对象调用,函数中的 this 指向该对象。
const obj = {
name: 'obj',
say() {
console.log(this.name);
}
};
obj.say(); // 'obj'
当一个函数被多层对象调用,函数中的 this 指向最近的那个对象。
function foo() {
console.log(this.a);
}
var obj = {
a: 1,
foo: foo,
};
var oo = {
a: 2,
foo: obj,
};
oo.foo.foo(); // 1 离 foo 最近的对象是 obj 所以 this 指向 obj 对象
3.显示绑定
JavaScript中提供了三种方法 call, apply 和 bind,可以强制指定函数中的this。
-
call:立即调用函数,参数逐个传递。
fn.call(obj, x, y) -
apply:立即调用函数,参数以数组方式传递。
fn.apply(obj, [x, y] -
bind:返回一个新的函数。
fn.bind(obj, x, y)()
4.new 绑定
当一个函数被用作构造函数时, this 会指向新创建的那个实例对象。new的过程如下:
-
创建一个全新的空对象。
-
将这个空对象的
__proto__指向构造函数的prototype; -
将构造函数内的
this绑定到该新对象; -
执行构造函数代码;
-
如果构造函数没有返回对象,则返回这个新对象。
function Person(name) {
this.name = name;
this.say = function() {
console.log(this.name);
};
}
const alice = new Person('Alice');
alice.say(); // 'Alice',this 指向 alice 实例
四.箭头函数中的 this
箭头函数没有 this 这个概念,所以写在箭头函数中的 this ,是它外层非箭头函数的。
function foo() {
var fn = () => {
this.a = 2;
};
fn();
}
var obj = {
a: 1,
bar: foo,
};
obj.bar(); // this 指向的是 obj 对象,所以相当于最后执行obj.a = 2,obj 对象中 a 的值变为 2
console.log(obj); //obj { a: 2, bar: foo}
注意:如果箭头函数外面也没有普通函数,那么 this 指向全局
const fn = () => {
console.log(this); // window
};
fn();
五.一表总结
| 绑定类型 | 规则简述 | 示例场景 |
|---|---|---|
| 默认绑定 | 独立调用函数,this 指向全局 | fn() |
| 隐式绑定 | 通过对象调用,this 指向该对象(最近的那个) | obj.fn() |
| 显式绑定 | 使用 call/apply/bind 强制指定 this | fn.call(obj) |
| new 绑定 | 作为构造函数调用,this 指向新实例 | new Foo() |
| 箭头函数 | 没有自己的 this,继承外层非箭头函数的 this | () => console.log(this) |