前端面试系列-JS篇-this指向

1,029 阅读5分钟

相信大家在刚开始的时候都会对于这奇怪的 this 指向感到很困惑,this 指向为什么是动态?它又有什么规律呢?通过这篇文章,这些问题都可以得到解答。

什么是 this

JavaScript 中,this 是一个关键字,它指向当前执行上下文中的对象。执行上下文可以是函数执行上下文,也可以是全局执行上下文。this 关键字是在函数调用时确定的,因此它的指向是动态的而不是静态的。

为什么会有 this 指向问题

this 关键字的使用是基于函数式编程的概念。在函数式编程中,this 关键字被赋予了特殊的含义,它不再是指向调用者的简单指针,而是表示函数当前运行的环境,也就是 this 关键字的值是在函数运行时动态确定的。 那为什么 JavaScript 中函数的调用方式和上下文是动态的呢?

这是因为在 JavaScript 中,函数作为一等公民,可以作为变量、对象的属性、数组元素、参数等进行传递。这种灵活性使得 JavaScript 可以在不同的上下文中使用相同的代码,并且可以在运行时动态地改变函数的调用方式和上下文。

例如,可以使用 call、apply 或 bind 方法来手动指定函数执行时的 this 值,从而改变函数的上下文。可以将函数作为对象的方法调用,从而改变函数的调用方式。可以将函数作为参数传递给其他函数,从而在其他函数中调用该函数并改变函数的上下文。

这种动态性使得 JavaScript 可以灵活地处理不同的情况,并且可以在运行时动态地改变代码行为。但是,这也会带来一些问题,因为函数的调用方式和上下文是动态的,因此可能会出现意外的 this 指向问题。为了避免这种问题,我们需要小心地处理函数的调用方式和上下文,并根据需要手动指定函数执行时的 this 值。

this的指向

  • 函数调用模式下,this 永远指向离它最近的调用对象,如果没有调用对象,this 指向 window
  • 构造函数模式下,构造函数中的 this 指向实例化对象
  • 箭头函数中的 this 指向的是外部作用域的 this
  • apply,call,bind 调用模式下,this 指向第一个参数;

函数调用模式

this 永远指向离它最近的调用对象,如果没有调用对象,this 指向 window

我们先从简单的开始

function print() {
    console.log(this) 
}
print();

这里的 this 指向的是 window ,为什么呢?

我们可以理解成 print 本身就是全局方法,挂载在 window 下,所以 print() 也就等于 window.print() ,那么 print 方法的调用对象就是 windowthis 也就指向 window

我们继续,加一点难度

function print() {
    console.log(this) 
}
let obj = {
  print,
}
obj.print();

这里的this指向的是obj,因为print方法的调用对象就是obj,所以this指向的是obj

继续加大难度


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

foo()

大家知道这里的 this 指向谁吗?是 window 还是 foo

3,2,1 揭晓答案

这里的 this 指向的是 window ,不要被他繁杂的代码所迷惑,就记住在函数调用模式下,this指向离它最近的调用对象,所以 inner 执行的时候没有调用对象,那么指向的就是 window

好了,调用模式下讲完了,我们接下来继续构造函数模式的 this 指向问题

构造函数模式

构造函数中的 this 指向实例化对象

function Person(name) {
  this.name = name;
}

const person = new Person("Alice");
console.log(person.name); 

这里的 this 指向新创建的 Person 对象,person.name打印出来自然是 Alice

在构造函数被调用时,JavaScript 引擎会创建一个新的对象并将其赋值给 this ,然后执行构造函数中的代码,并最终返回 this。详情可查看前端面试系列-JS篇-手写 new

箭头函数中的 this

箭头函数中的 this 指向的是外部作用域的 this

与普通函数不同,箭头函数的 this 指向在函数定义时就确定了,而不是在函数调用时确定。箭头函数的 this 指向最接近的包含它的非箭头函数的 this 指向。如果箭头函数没有包含在任何非箭头函数中,则 this 指向全局对象(在浏览器环境中是 window 对象)。

技巧:它的外层没有函数,thiswindow;外层有函数,看外层函数的 this 是谁,它的 this 就是谁。

function outerFunction() {
  const arrowFunction = () => {
    console.log(this); // 指向外部函数作用域中的 this
  };

  arrowFunction();
}

outerFunction();

在这个例子中,箭头函数 arrowFunction 是在 outerFunction 函数内部定义的,因此它的 this 指向的是 outerFunctionthis,那 outerFunctionthis 指向谁呢?

outerFunction 的调用者,大家明白了吗?是 window ,不明白的要好好去看第一条哦

apply,call,bind 调用模式

apply,call,bind 调用模式下,this 指向第一个参数。

这里先简单介绍一下 apply,call,bind详情可查看

image.png

通过使用callapply方法,可以指定函数执行时的 this 指向。例如:

function sayHello() {
  console.log(`Hello, my name is ${this.name}.`);
}

const person1 = { name: "Alice" };
const person2 = { name: "Bob" };

sayHello.call(person1); // this指向person1对象
sayHello.apply(person2); //this指向person2对象

总结

JavaScript 中,this 关键字指向当前执行上下文中的对象。在全局作用域中,this 指向全局对象(在浏览器环境中是 window 对象)。在函数中,this 的指向可能会根据调用方式而变化。在对象方法调用和构造函数调用中,this 指向当前对象;在使用callapply方法调用时,可以手动指定 this 的指向。在箭头函数中,this 的指向在定义时就确定了,最接近的包含它的非箭头函数的 this指向,如果没有,则指向全局对象。

看完这篇文章,相信大家都对 this 指向有了一个全新的认识,使用的话也不会对 this 指向感到困惑了,这样也能愉快的 coding 了

最后祝大家变得更强!