一道this 面试题解析

50 阅读3分钟

JavaScript 中的 this:从丢失到重构

在 JavaScript 开发中,this 的指向问题往往是初学者甚至是资深开发者都会踩到的坑。理解其背后的逻辑,不仅能提升代码健壮性,更是通往高级程序员的必经之路。

一、 核心概念:this 到底指向谁?

在 JavaScript 中,this 的指向并非在函数定义时确定,而是在执行时确定的。

  • 全局环境:在浏览器环境下,变量环境中的全局变量(如 var name = "windowName")本质上是挂载在 window 对象下的。
  • 对象调用:当函数作为对象的方法被调用时(如 a.func2()),this 指向调用该方法的对象 a
  • 默认绑定:普通函数在独立调用时,其 this 默认指向全局对象 window

二、 经典陷阱:定时器中的 this 丢失

我们经常会在异步回调中发现 this “莫名其妙”地失效了。参考以下典型案例:

var a = {
    name: 'cherry',
    func2: function() {
        setTimeout(function() {
            console.log(this);  // 这里的 this 指向 window
            this.func1();       // 报错:this.func1 is not a function
        }, 1000)
    }
}

原因分析:虽然 func2 是由 a 调用的,但 setTimeout 内部的匿名函数是在全局执行栈中被调用的。根据默认绑定规则,此时的 this 指向了 window

三、 四种解决方案:重回掌控

为了让 this 回到预想的轨道上,我们可以采取以下策略:

1. 显式绑定:call 与 apply

通过 callapply 可以立即改变函数执行时的 this 指向。

  • 特点:立即执行函数,并强行指定上下文。
  • 示例:在 setTimeout 中使用 .call(a) 会让回调函数立即在 a 的作用域下执行。

2. 预绑定:bind

如果你不想立即执行,而是想在将来某个时刻(如定时器到期时)以特定指向执行,bind 是最佳选择。

  • 特点:它会返回一个绑定了指定对象的新函数,而不会立即触发执行。

3. 作用域链:that = this

这是最经典的老派做法,通过在外部作用域缓存 this

  • 原理:在 func2 中定义 var that = this;,利用闭包和作用域链的特性,内部匿名函数通过访问 that 变量来操作 a 对象。

4. 终极方案:箭头函数

在现代 ES6 开发中,箭头函数是解决 this 问题的首选。

  • 关键特性:箭头函数放弃了自己的 this
  • 机制:它没有自己的 this 指向,也不会创建执行上下文,更没有 arguments 对象。它会直接借用定义时所在作用域的 this

四、 总结

方法是否立即执行核心特性
call / apply强行改变当前执行环境
bind以后再执行,预先绑定上下文
that = this依赖作用域链寻找变量
箭头函数随调用情况彻底放弃 this,向上级捕获

掌握这些规则,你就能在复杂的异步逻辑和对象方法中游刃有余地处理 this 指向,避免那些令人头疼的运行时错误。