import{_ as s,o as n,c as a,a as l}from"./app.6cf9e426.js";const u=JSON.parse('{"title":"一文读懂函数中this指向问题","description":"","frontmatter":{},"headers":[{"level":2,"title":"函数中this指向","slug":"函数中this指向","link":"#函数中this指向","children":[]},{"level":2,"title":"如何改变this的指向","slug":"如何改变this的指向","link":"#如何改变this的指向","children":[{"level":3,"title":"new 实例化一个函数","slug":"new-实例化一个函数","link":"#new-实例化一个函数","children":[]},{"level":3,"title":"使用 call apply bind","slug":"使用-call-apply-bind","link":"#使用-call-apply-bind","children":[]}]},{"level":2,"title":"箭头函数","slug":"箭头函数","link":"#箭头函数","children":[{"level":3,"title":"箭头函数中的this","slug":"箭头函数中的this","link":"#箭头函数中的this","children":[]},{"level":3,"title":"箭头函数中this的查找规则","slug":"箭头函数中this的查找规则","link":"#箭头函数中this的查找规则","children":[]},{"level":3,"title":"严格模式","slug":"严格模式","link":"#严格模式","children":[]}]},{"level":2,"title":"this面试题","slug":"this面试题","link":"#this面试题","children":[]}],"relativePath":"article/一文读懂函数中this指向问题.md","lastUpdated":1676469217000}'),e={name:"article/一文读懂函数中this指向问题.md"},p=l(`

一文读懂函数中this指向问题

函数中this指向

函数在调用时, Javascript会默认为this绑定一个值

// 定义一个函数
function foo() {
  console.log(this)
}

// 1. 直接调用
foo() // Window

// 2. 绑定对象调用
const obj = { name: 'ziu', aaa: foo }
obj.aaa() // obj

// 3. 通过call/apply调用
foo.call('Ziu') // String {'Ziu'}

this的绑定:

this始终指向最后调用它的对象

function foo() {
  console.log(this)
}
foo() // Window

const obj = {
  name: 'ziu',
  bar: function () {
    console.log(this)
  }
}
obj.bar() // obj

const baz = obj.bar
baz() // Window

如何改变this的指向

new 实例化一个函数

new一个对象时发生了什么:

  1. 创建一个空对象
  2. 这个空对象会被执行prototype连接
  3. 将this指向这个空对象
  4. 执行函数体中的代码
  5. 没有显式返回这个对象时 会默认返回这个对象

函数可以作为一个构造函数, 作为一个类, 可以通过new关键字将其实例化

function foo() {
  console.log(this)
  this.name = 'Ziu'
}
foo() // 直接调用的话 this为Window

new foo() // 通过new关键字调用 则this指向空对象

使用 call apply bind

在 JavaScript 中, 函数是对象。

JavaScript 函数有它的属性和方法。call() 和 apply() 是预定义的函数方法。

两个方法可用于调用函数,两个方法的第一个参数必须是对象本身


要将foo函数中的this指向obj,可以通过赋值的方式:

obj.foo = foo // 绑定
obj.foo() // 调用

但是也可以通过对函数调用call / apply实现

var obj = {
  name: 'Ziu'
}

function foo() {
  console.log(this)
}

foo.call(obj) // 将foo执行时的this显式绑定到了obj上 并调用foo
foo.call(123) // foo的this被绑定到了 Number { 123 } 上
foo.call("ziu") // 绑定到了 String { "ziu" } 上

包装类对象

当我们直接使用类似:

"ZiuChen".length // String.length

的语句时,JS会为字符串 ZiuChen 包装一个对象,随后在这个对象上调用 .length 属性

call和apply的区别

function foo(name, age, height) {
  console.log(this)
}

foo('Ziu', 18, 1.88)

foo.apply('targetThis', 'Ziu', 18, 1.88)

foo.call('targetThis', ['Ziu', 18, 1.88])

当我们需要给一个带参数的函数通过call/apply的方式绑定this时,就需要使用到call/apply的第二个入参了。

参数列表

当传入函数的参数有多个时,可以通过...args将参数合并到一个数组中去

function foo(...args) {
  console.log(args)
}

foo("Ziu", 18, 1.88) // ["Ziu", 18, 1.88]

bind绑定

如果我们希望:在每次调用foo时,都能将obj绑定到foothis上,那么就需要用到bind

// 将obj绑定到foo上
const fun = foo.bind(obj)
// 在后续每次调用foo时, foo内的this都将指向obj
fun() // obj
fun() // obj

bind()方法将创建一个新的函数,当被调用时,将其this关键字

箭头函数

箭头函数是ES6新增的编写函数的方式,更简洁。

箭头函数中的this

在箭头函数中是没有this的:

const foo = () => {
  console.log(this)
}
foo() // window
console.log(this) // window

之所以找到了Window对象,是因为在调用foo()时,函数内部作用域并没有找到this,转而向上层作用域找this

因此找到了顶层的全局this,也即Window对象

箭头函数中this的查找规则

检查以下代码:

const obj = {
  name: "obj",
  foo: function () {
    const bar = () => {
      console.log(this)
    }
    return bar
  }
}
const fn = obj.foo()
fn() // obj

代码执行完毕,控制台输出this值为obj对象,这是为什么?

箭头函数中没有this,故会向上层作用域寻找thisbar的上层作用域为函数foo,而函数foothis由其调用决定

调用foo函数的为obj对象,故内部箭头函数中的this指向的是obj

检查以下代码:

const obj = {
  name: "obj",
  foo: () => {
    const bar = () => {
      console.log(this)
    }
    return bar
  }
}
const fn = obj.foo()
fn() // Window

和上面的代码不同之处在于:foo也是由箭头函数定义的,bar向上找不到foothis,故而继续向上,找到了全局this,也即Window对象

严格模式

this面试题

var name = 'window'

var person = {
  name: 'person',
  sayName: function () {
    console.log(this.name)
  }
}

function sayName() {
  var sss = person.sayName

  sss() // 默认绑定: window

  person.sayName();  // 隐式绑定: person

  (person.sayName)() // 隐式绑定: person, 本质与上一行代码相同

  ;(person.sayName = person.sayName)() // 间接调用: window
}

sayName()
var name = 'window'

var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => console.log(this.name)
  }
}

var person2 = {
  name: 'person2'
}

person1.foo1() // 隐式绑定: person1
person1.foo1.call(person2) // 显式绑定: person2

person1.foo2() // 上层作用域: window
person1.foo2.call(person2) // 上层作用域: window

person1.foo3()() // 默认绑定: window
person1.foo3.call(person2)() // 默认绑定: window
person1.foo3().call(person2) // 显式绑定: person2

person1.foo4()() // 隐式绑定: person1
person1.foo4.call(person2)() // 显式绑定: person2
person1.foo4().call(person2) // 隐式绑定: person1
`,59),o=[p];function c(i,r,t,b,C,d){return n(),a("div",null,o)}const m=s(e,[["render",c]]);export{u as __pageData,m as default};