JS十大难点四

this
this 是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用

this 的四种绑定规则

在 JavaScript 中,影响 this 指向的绑定规则有四种:

1
2
3
4
默认绑定
隐式绑定
显式绑定
new 绑定

默认绑定

这是最直接的一种方式,就是不加任何的修饰符直接调用函数

1
2
3
4
5
6
7
8
function foo() {
console.log(this); //window
console.log(this.a) // 输出 a,为2
}
var a = 2; // 变量声明到全局对象中
foo();

隐式绑定

这种情况会发生在调用位置存在「上下文对象」的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo() {
console.log(this.a);
}
let obj1 = {
a: 1,
foo,
};
let obj2 = {
a: 2,
foo,
}
obj1.foo(); // 输出 1
obj2.foo(); // 输出 2

当函数调用的时候,拥有上下文对象的时候,this 会被绑定到该上下文对象。
正如上面的代码,
obj1.foo() 被调用时,this 绑定到了 obj1,
而 obj2.foo() 被调用时,this 绑定到了 obj2

显式绑定

new 绑定

最后一种,则是使用 new 操作符会产生 this 的绑定。
在理解 new 操作符对 this 的影响,首先要理解 new 的原理。
在 JavaScript 中,new 操作符并不像其他面向对象的语言一样,而是一种模拟出来的机制。
在 JavaScript 中,所有的函数都可以被 new 调用,这时候这个函数一般会被称为「构造函数」,实际上并不存在所谓「构造函数」,更确切的理解应该是对于函数的「构造调用」

使用 new 来调用函数,会自动执行下面操作

1、创建一个全新的对象。
2、这个新对象会被执行 [[Prototype]] 连接。
3、这个新对象会绑定到函数调用的 this。
4、如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
所以如果 new 是一个函数的话,会是这样子的

1
2
3
4
5
6
7
8
9
10
11
function New(Constructor, ...args){
let obj = {}; // 创建一个新对象
Object.setPrototypeOf(obj, Constructor.prototype); // 连接新对象与函数的原型
return Constructor.apply(obj, args) || obj; // 执行函数,改变 this 指向新的对象
}
function Foo(a){
this.a = a;
}
New(Foo, 1); // Foo { a: 1 }

所以,在使用 new 来调用函数时候,我们会构造一个新对象并把它绑定到函数调用中的 this 上

this 到底是什么

this 并不是在编写的时候绑定的,而是在运行时绑定的。它的上下文取决于函数调用时的各种条件。
this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个「执行上下文」,这个上下文会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this 就是这个记录的一个属性,会在函数执行的过程中用到