深入this指向

49 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

我们知道,this代表的是函数的指向。那么我们也同时需要明白一个问题就是,函数的this,是在什么时间确认的。

前面我们学习执行上下文的过程中,有提到关于this的信息,在执行上下文的环境记录对象中,存在一个内部属性[[ThisValue]]用于记录this指向,同时还存在一个内部方法BindThisValue用于设置[[ThisValue]]的值。这也就是说,this的指向,对于函数来说是在执行上下文的创建过程中确定的。因此,这也是为什么说,this的指向是在函数调用的时候才确定的。

const a = 20

const obj = {
    a: 20
}

function fn(){
    console.log(this.a)
}

fn() // 10
fn.call(obj) // 20

通过a的值的不同体现,我们知道this分别指向window对象和obj对象。

全局上下文中的this

在全局上下文中,this对象指向全局对象本身

this.a = 20

var b = 10

c = 30

const d = 40 // const,let声明的不会挂载在全局对象上

console.log(a) // 20
console.log(b) // 10
console.log(c) // 30
console.log(d) // undefined

函数中的this

函数上下文中的this,和调用该函数的方式息息相关。

在一个函数执行上下文中,this由该函数的调用者caller提供,由调用的方式决定。

  • 有明确的调用者
  • 独立调用,无明确调用者
function fn(a){
    console.log(arguments.callee)
    console.log(this)
}

window.fn(10)

很显然当前window是调用者callee,fn是被调用者caller,因此fn内部的this指向window。但同时还有一种写法:

function fn(a){
    console.log(arguments.callee)
    console.log(this)
}

fn(10)

此时需要分情况来讨论,在严格模式下,fn此时没有调用者,因此this指向undefined,正在非严格模式下,fn的this依然默认由window来承担。

call/apply/bind显示指定this

js提供了call/apply/bind方法显示的设置this的指向,所有函数均可以调用此方法。

var a = 20

var object = {
    a: 40
}

function fn() {
    console.log(this.a)
} 

如果我们正常独立直接调用fn,在非严格模式下this会指向window对象,因此函数输出20,但我们还可以通过显示指定的方法设置fn的调用者。

fn.call(object) // 40
fn.apply(object) // 40

在调用call/apply时,实际上还是执行fn本身,只是将函数中的this指向设置为传入的第一个参数,它们的区别就是参数传递方式不同。

call的第一个参数是函数内部指定的this指向,后续参数是函数本身执行需要的参数,一个一个进行传递,

apply的第一个参数和call一样,而第二个参数则是函数本身需要的全部参数,整合为一个数组进行传递。

function fn(a,b){
    return this.g + a + b
}

var g = 1
var object = {g: 2}

fn(10,10) // 21

fn.call(object,10,10) // 22
fn,apply(object,[10,10]) // 22

bind方法也能显示指定函数内部this指向,但是它和call/apply仍有不同。

当函数调用bind时,函数不会立即执行,而是返回一个新的函数,这个函数和原函数一样,但是并非原函数,新函数的this指向已经被绑定,参数为bind的后续参数。

function fn(a,b){
     return this.g + a + b
}

var g = 1
var object = {g: 2}

var _fn = fn.bind(object,1,1)

console.log(_fn === fn) //false
_fn() // 4
_fn(2,2) // 6