JS基础巩固系列:【1】this的指向问题

普通的调用场景

1
2
3
4
5
6
7
8
9
10
11
function foo() {
console.log(this.a)
}
var a = 1;
foo();
const obj = {
a: 2,
foo: foo
};
obj.foo();
const c = new foo();

我们针对上面几个场景分析:

  1. 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
  2. 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
  3. 对于 new 的方式来说,this 被永远绑定在了 c 上面,不会被任何方式改变 this

特殊情况

箭头函数

1
2
3
4
5
6
7
8
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())

要理解上面这种情况,首先我们要明白几个知识点:

  1. 箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this,对于上面的例子,包裹箭头函数的第一个普通函数是 a,所以此时的this是window。
  2. 对于箭头函数,使用bind这类函数是无效的

bind

1
2
3
4
5
6
7
let a = { 
name: 'test'
};
function foo() {
console.log(this)
}
foo.bind(a)();
  • 使用bind一类可以改变上下文的API,this 取决于第一个参数,如果第一个参数为空,那么就是 window。
1
2
3
4
5
let a = {}
let fn = function () {
console.log(this)
}
fn.bind().bind(a)()
  • 对于上面的例子,如果对一个函数进行多次bind,这个时候的this会是谁呢?

上述代码可以转换成另外一种方式

1
2
3
4
5
6
7
// fn.bind().bind(a) 等于
let fn2 = function fn1() {
return function() {
return fn.apply()
}.apply(a)
}
fn2()

从这个函数来看,我们可以发现,不管给函数 bind 了多少次,fn中的this,永远是由第一次 bind 决定的,所以这里this的结果永远是 window。

以上就是 this 的规则了,但是可能会发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this 最终指向哪里。

首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。

上面的逻辑,汇总到流程图中的规则如下,仅适用单个规则的情况:
Image text

顺便提一下,js可以改变上下文的API包含 apply、call以及bind,三者的关系如下

  • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
  • apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文。非严格模式下,如果第一个参数传入null或者undefined,this会指向window;
  • apply 、 call 、bind 三者都可以利用后续参数传参,apply第二个参数是一个参数数组,call接受一个参数列表;
  • bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用。