「前端每日一问(24)」说一下 JS 中的 this

399 阅读5分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

本题难度:⭐ ⭐

答:

面向对象语言中 this 表示当前对象的一个引用,而 JS 中的 this 是完全不同的概念。

可以把JS中的 this 分为两种,一种是全局上下文中的 this,一种是函数上下文中的this。

全局上下文的 this 指向 window。

函数上下文的 this 指向不是固定不变的,取决于函数处于什么位置、以什么方式调用,可以总结成如下图:

image.png

当然,还有一个优先级问题,优先级是new 调用 > call、apply、bind 调用 > 对象上的函数调用 > 普通函数调用。

全局上下文中的 this

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

console.log(this === window) // true

age = 18
console.log(window.age) // 18
console.log(this.age) // 18

this.name = '阿林'
console.log(window.name) // '阿林'
console.log(name) // '阿林'

函数上下文中的this

函数上下文中的 this 与 arguments一样,就是函数的隐式参数,可以在任意函数中调用。

函数中的 this 的值不是固定不变的,取决于函数处于什么位置、以什么方式调用

全局上下文中的函数

直接调用全局上下文中的函数,this 指向默认情况下为 window

function fn () {
  console.log(this) // window
}
fn()
function fn () {
  var a = 1
  console.log(this.a) // 2
}
var a = 2
fn()

严格模式下为 undefined

'use strict'

function fn () {
  console.log(this) // undefined
}
fn()

对象中的函数

调用对象中的函数,this 指向为这个对象。

const obj = {
  a: 1,
  fn () {
    console.log('this :>> ', this)
    console.log('this.a :>> ', this.a)
  }
}

obj.fn() 

image.png

但是如果函数嵌套有函数,此时的 this 指向为 window,就非常令人迷惑。

const obj = {
  a: 1,
  fn () {
    return function () {
      console.log('this :>> ', this)
      console.log('this.a :>> ', this.a)
    }
  }
}

var a = 100

obj.fn()()

image.png

其实可以这么理解:

obj.fn()()

等价于

const temp = obj.fn() // 定义一个临时变量来存储 obj.fn 返回的函数
temp() // 执行这个函数

上面代码示例中的 temp 在运行时是处在 window 环境中的,所以 this 指向 window。

遇到对象里有函数嵌套函数的情况,想要 this 指向这个对象,es6之前,可以用一个临时变量 _this 来暂存 this,

const obj = {
  a: 1,
  fn () {
    const _this = this
    return function () {
      console.log('this :>> ', _this)      // 输出 obj
      console.log('this.a :>> ', _this.a)  // 输出 1
    }
  }
}

obj.fn()()

箭头函数

接上例,对象里有函数嵌套函数的情况,想要 this 指向这个对象,也可以用箭头函数。

const obj = {
  a: 1,
  fn () {
    return () => {
      console.log('this :>> ', this)      // 输出 obj
      console.log('this.a :>> ', this.a)  // 输出 1
    }
  }
}

obj.fn()()

对于普通函数来说,内部的 this 指向函数运行时所在的对象。

对于箭头函数,它不会创建自己的 this,它只会从自己的作用域链的上一层继承 this

所以这里 fn 中嵌套的匿名箭头函数中的 this,指向它作用域链的上一层的 this,也就是函数 fn 的 this,也就是 obj。

其实,箭头函数内部实现也是定义临时变量 _this 来暂存 this,不信你用 babel es6 转 es5看一看:

image.png

babel 在线地址

构造函数

构造函数内,如果返回值是一个对象,this 指向这个对象,否则 this 指向新建的实例。

function Person (name) {
  this.name = name
}
const p = new Person('lin')
console.log(p.name) // 'lin'
function Person (name) {
  this.name = name
  return {
    name: 'xxx'
  }
}
const p = new Person('lin')
console.log(p.name) // 'xxx'
function Person (name) {
  this.name = name
  return {}
}
const p = new Person('lin')
console.log(p.name) // 'undefined'

如果有返回值,但是不是一个对象,this 还是指向实例。

function Person (name) {
  this.name = name
  return 123
}
const p = new Person('lin')
console.log(p.name) // 'lin'

函数中 this 的值可以显式改变

可以使用 call、apply 和 bind 来显式改变函数中的 this 指向。

call

Function.prototype.call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

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

const obj = {
  name: '阿林'
}
fn.call(obj) // 指定 this 为 obj,输出 '阿林'

使用 call ,改变构造函数中 this 的指向,可以实现继承

function Person (name, age) {
  this.name = name
  this.age = age
}

function Student (name, age, grade) {
  Person.call(this, name, age) // 调用 Person 构造函数,指定 this 为 Student 实例,实现继承
  this.grade = grade
}

const s1 = new Student('阿林', 18, 100)
console.log(s1)

apply

Function.prototype.apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

apply 和 call 的功能完全一样,只是传参形式不一样,call 是传多个参数,apply 是只传参数集合。

// 使用 call
function add (x, y, z) {
  return this.x + this.y + this.z
}

const obj = {
  x: 1,
  y: 2,
  z: 3
}

console.log(add.call(obj, 1, 2, 3)) // 6
// 使用 apply
function add (x, y, z) {
  return this.x + this.y + this.z
}

const obj = {
  x: 1,
  y: 2,
  z: 3
}

console.log(add.apply(obj, [1, 2, 3])) // 输出 6,只是传参形式不同而已

bind

Function.prototype.bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

bind 和 call、apply 的区别是,函数调用 call 和 apply 会直接调用,而调用 bind 是创建一个新的函数,必须手动去再调用一次,才会生效。

function add (x, y, z) {
  return this.x + this.y + this.z
}

const obj = {
  x: 1,
  y: 2,
  z: 3
}

console.log(add.bind(obj, 1, 2, 3)) // 输出创建的函数

const add1 = add.bind(obj, 1, 2, 3)
console.log(add1()) // 输出 6

结尾

全局上下文的 this 指向 window。

函数上下文的 this 指向不是固定不变的,取决于函数处于什么位置、以什么方式调用,再看一遍这张图,加深记忆:

image.png

如果我的文章对你有帮助,你的👍就是对我的最大支持^_^

我是阿林,输出洞见技术,再会!

上一篇:

「前端每日一问(23)」实现一个函数,每调用一次,函数输出值 +1

下一篇:

「前端每日一问(25)」说一下函数形参、实参、剩余参数、默认参数、隐式参数