this
由于 JavaScript 的设计原理:在函数中,可以引用运行环境中的变量。因此就需要一个机制来让我们可以在函数体内部获取当前的运行环境,这就是 this
。
因此要明白 this
指向,其实就是要搞清楚函数的运行环境,说白了就是,谁调用了函数。
不同情况的调用,this
指向也是不同的。我们先来看几个函数调用的场景:
function foo () {
console.log(this, this.?a);
}
let a = 1;
foo(); // Window{} 1
let obj = {
a: 2,
foo
}
obj.foo(); // {a: 2, foo: ƒ} 2
let c = new foo(); // foo {} undefined
- 直接调用
foo
,就相当于window.foo()
,this
=>Window
- 对于
obj.foo()
来说,你只要记住,谁调用了foo
,谁就是this
,所以this
=>obj
- 对于
new
方式调用,this
永远指向c
,不会被任何方式改变this
了解了以上几种情况之后, 其实大多数代码中的 this
就没啥问题了,下面我们来看箭头函数中的 this
function fn() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(fn()()()) // Window{}
- 首先箭头函数其实是没有
this
的,它里面的this
只取决于包裹箭头函数的第一个普通函数的this
。 - 在这个例子中,第一个普通函数为
fn
,所以此时的this
是Window
。 - 另外对箭头函数使用
bind
这类函数也是无效的。
最后一种情况就是 bind
这些改变上下文的 API 了,对于这些函数来说,this
取决于第一个参数,如果第一个参数为空,那么就为 Window
。
说到 bind
,如果对一个函数进行多次 bind
,会怎么样呢?
let fish = { cat: 1 }
let fn = function () {
return console.log(this)
}
fn.bind().bind(fish)()
如果你认为输出结果是 fish 那就错了,我们可以换成另一种形式:
let fn2 = function fn1() {
return function () {
return fn.apply()
}.apply(fish)
}
fn2()
由此可以看出,不管我们给函数 bind
多少次,fn 中的 this
永远都是有第一个 bind
决定的,所以最后的结果是 Window
。
let fish = { cat: 1 }
let fn = function () {
return console.log(this.cat)
}
fn.bind(fish)() // 1
以上就是 this
的规则了,但是可能会出现多个规则同时存在的情况,这时候不同的规则之间会根据优先级来决定 this
的最终指向哪里。
首先,new
的方式优先级最高,其次是 bind
这些函数,然后是 obj.foo()
这种调用方式,最后是 foo
这种调用方式。
同时,箭头函数的 this
一旦被绑定,就不会再被任何方式所改变。
但是,光是以上简单的几种机制并不能满足我们的日常需求,因此提供了三种方式来手动改变 this
指向:
func.bind(target, param1, param2, ...)
func.call(target, param1, param2, ...)
func.apply(target, [param1, param2, ...])