关于this的指向

100 阅读5分钟

关于this的指向

create by db on 2021-3-29 18:47:18
Recently revised in 2022-7-20 16:38:42

闲时要有吃紧的心思,忙时要有悠闲的趣味

目录

前言

返回目录

正文

  this是js中的一个的关键字,在函数运行时自动生成一个内部对象。this的指向不是在函数创建的时候确定下来的,而是在函数执行时确定的。

关于 this 的指向

返回目录

  如果要判断一个运行中函数的 this 指向,就需要找到这个函数的直接调用位置。找到之后可以应用四条规则来判断 this 的绑定对象。

1. new 绑定

  构造函数,使用 new 来调用 foo(..)时,我们会构造一个新对象并且把它绑定到 foo(..)调用中的 this 上,即此时的 this 指向新创建的对象。此时的绑定行为我们称之为new 绑定。 示例如下:

var a = 2

function foo(a) {
    this.a = a
}
var obj = new foo(3)
console.log('a的值是', obj.a)
// a的值是 3

2. 显示绑定

  当使用函数的 callapply 或者 bind 方法绑定,会改变传入函数的 this。此时 this 指向被绑定的对象,即函数的第一个参数。

  因为可以直接指定 this 的绑定对象,因此我们称之为显式绑定

示例如下:

var a = 2
var obj = {
    a: 3,
}

function foo() {
    console.log('当前this指向', this)
    console.log('a的值是', this.a)
}
foo.call(obj)
// 当前this指向 {a: 2, foo: ƒ}   --> 直接调用形成隐式绑定,指向obj
// a的值是 3

3. 隐式绑定

  即被对象直接调用,此时 this 指向当前上下文对象。 示例如下:

var a = 2
var b = 3
var obj = {
    a: 4,
    foo: foo,
}

function foo() {
    console.log('当前this指向', this)
    console.log('a的值是', this.a)
    console.log('b的值是', this.b)
}
obj.foo()
// 当前this指向 {a: 2, foo: ƒ}   --> 直接调用形成隐式绑定,指向obj
// a的值是 4
// b的值是 undefined             --> obj没有b这个属性,所以输出undefined

4. 默认绑定

  如果以上绑定都不存在,或者说函数不带任何修饰被直接调用,那就属于默认绑定,此时分为两种情况:

  1. 非严格模式下,this指向 全局对象,即window

示例如下:

var a = 2
let b = 3

function foo() {
    console.log('当前this指向', this)
    console.log('a的值', this.a)
    console.log('b的值', this.b)
}
foo()
// 当前this指向 Window {window: Window, self: Window, document: document…}
// a的值是 2
// b的值是 undefined     --> 因为let在全局作用域中声明的变量不会成为window对象的属性
  1. 严格模式下,this指向 undefind

示例如下:

'use strict'
var a = 2

function foo() {
    console.log('当前this指向', this)
    console.log('a的值是', this.a)
}
foo()

// 当前this指向 undefined
// TypeError: Cannot read property 'a' of undefined

箭头函数调用

  不得不说,ES6 提供了箭头函数,增加了我们的开发效率,但是在箭头函数里没有 this ,箭头函数里面的 this 是继承外面的环境。

普通函数中:

var a = 2
var obj = {
    a: 3,
    fn: function() {
        console.log('外部函数this指向', this)
        console.log('外部函数a的值是', this.a)
        setTimeout(function() {
            console.log('内部函数当前this指向', this)
            console.log('内部函数a的值是', this.a)
        })
    },
}
obj.fn()

var a = 2
var obj = {
    a: 3,
    fn: () => {
        console.log('外部函数this指向', this)
        console.log('外部函数a的值是', this.a)
    },
}
obj.fn()

// 外部函数this指向 {a: 3, fn: ƒ}
// 外部函数a的值是 3

// 内部函数当前this指向 Window {0: Window, 1: global…}
// 内部函数a的值是 2

  不难发现,虽然 fn() 里面的 this 是指向 obj ,但是,传给 setTimeout 的是普通函数this 指向是 widow

换成箭头函数:

var a = 2
var obj = {
    a: 3,
    fn: function() {
        console.log('外部函数this指向', this)
        console.log('外部函数a的值是', this.a)
        setTimeout(() => {
            console.log('内部函数当前this指向', this)
            console.log('内部函数a的值是', this.a)
        })
    },
}
obj.fn()

// 外部函数this指向 {a: 3, fn: ƒ}
// 外部函数a的值是 3

// 内部函数当前this指向 {a: 3, fn: ƒ}
// 内部函数a的值是 3

  这次两个函数输出相同,以为传给 setTimeout 的是箭头函数,然后箭头函数里面没有 this ,所以要向上层作用域查找,在这个例子上, setTimeout 的上层作用域是 fn。而 fn 里面的 this 指向 obj ,所以 setTimeout 里面的箭头函数的 this ,指向 obj 。

优先级

  直接说结论:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

总结

返回目录

  我们可以根据以下顺序进行判断this指向:

  1. 构造函数中, this 指向新创建的对象

  2. 当使用函数的 callapply 或者 bind 方法绑定 this 指向被绑定的对象,即函数的第一个参数。

  3. 被对象直接调用,谁调用该函数this指向就指向谁

  4. 以上都不是的话,在非严格模式下,this指向 window

  5. 在严格模式下,this指向 undefined

  6. 箭头函数里没有自己的 this ,箭头函数里面的 this 继承外面的环境。

后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址

文档协议


db 的文档库db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。