JavaScript 执行上下文视角下的 this 指向详解

作者:小括号时间:2026-10-26 11:07:34浏览:109

本文从 JavaScript 执行上下文的视角,详细解析 this 关键字的指向机制。内容涵盖全局、函数及 eval 执行上下文中的 this 差异,重点讲解通过 call/apply、对象方法调用及构造函数三种方式确定 this 的规则,并深入剖析嵌套函数中 this 丢失的问题及其通过变量保存或 ES6 箭头函数解决的方案。

前言

在对象内部的方法中访问对象自身的属性是常见需求。然而,JavaScript 的标准作用域机制无法直接满足这一需求,因此语言引入了独特的 this 机制来处理此类场景。

this 关键字与执行上下文紧密绑定,每个执行上下文都拥有独立的 this 值。JavaScript 的执行上下文主要分为全局执行上下文、函数执行上下文以及 eval 执行上下文,因此 this 的指向也对应分为全局环境中的 this、函数体内的 this 以及 eval 环境中的 this。

全局执行上下文中的 this

在全局执行上下文中,this 指向全局对象 window。这也是 this 与 JavaScript 作用域链的唯一交汇点。作用域链的最底层包含 window 对象,且全局执行上下文中的 this 同样指向 window 对象。

函数执行上下文中的 this

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

运行上述代码,控制台打印结果同样指向 window 对象。这表明在默认情况下,直接调用一个函数时,其执行上下文中的 this 指向全局 window 对象。

通常情况下,可以通过以下三种方式显式设置函数执行上下文中的 this 值:

1. 通过函数的 call 方法设置

let bar = {
  myName: "极客邦",
  test1: 1,
};
function foo() {
  this.myName = "极客时间";
}
foo.call(bar);
console.log(bar);
console.log(myName);

执行这段代码,你就能发现 foo 函数内部的 this 已经指向了 bar 对象,因为通过打印 bar 对象,可以看出 bar 的 myName 属性已经由“极客邦”变为“极客时间”了,同时在全局执行上下文中打印 myName,JavaScript 引擎提示该变量未定义。其实除了 call 方法,你还可以使用 bind 和 apply 方法来设置函数执行上下文中的 this。

2. 通过对象调用方法设置

var myObj = {
  name: "极客时间",
  showThis: function () {
    console.log(this);
  },
};
myObj.showThis();

运行示例代码可见,最终输出的 this 值指向 myObj。由此可得出结论:当使用对象调用其内部方法时,该方法执行上下文中的 this 指向该对象本身。

接下来稍微改变调用方式,将 showThis 方法赋值给一个全局变量后再进行调用,代码如下所示:

var myObj = {
  name: "极客时间",
  showThis: function () {
    this.name = "极客邦";
    console.log(this);
  },
};
var foo = myObj.showThis;
foo();

运行该代码会发现,此时 this 重新指向了全局 window 对象。

结论:

在全局环境中直接调用一个函数,函数内部的 this 指向全局变量 window。

通过对象调用其内部方法时,该方法执行上下文中的 this 指向该对象本身。

3. 通过构造函数中设置

function CreateObj() {
  this.name = "极客时间";
}
var myObj = new CreateObj();

当执行 new CreateObj() 时,JavaScript 引擎依次执行以下四个步骤:

首先,创建一个新的空对象 tempObj;

接着,调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数传入。这使得当 CreateObj 的执行上下文创建时,其 this 指向 tempObj 对象;

然后,执行 CreateObj 函数体。此时 CreateObj 函数执行上下文中的 this 指向 tempObj 对象;

最后,返回 tempObj 对象。

var tempObj = {};
CreateObj.call(tempObj);
return tempObj;

通过上述过程,利用 new 关键字成功构建了新对象,且构造函数内部的 this 即指向该新对象本身。

this 的设计缺陷以及应对方案

1. 嵌套函数中的 this 不会从外层函数中继承

var myObj = {
  name: "极客时间",
  showThis: function () {
    console.log(this);
    function bar() {
      console.log(this);
    }
    bar();
  },
};
myObj.showThis();

运行该代码后,内层函数 bar 中的 this 指向全局 window 对象,而外层函数 showThis 中的 this 指向 myObj 对象。

解决此问题的一种技巧是在外层 showThis 函数中声明一个变量 that 来保存外层 this 的值,然后在内层 bar 函数中使用该变量 that。本质上,这是将 this 的绑定机制转换为了词法作用域的查找机制。

此外,也可以使用 ES6 中的箭头函数来解决此问题:

var myObj = {
  name: "极客时间",
  showThis: function () {
    console.log(this);
    var bar = () => {
      console.log(this);
    };
    bar();
  },
};
myObj.showThis();

这是因为 ES6 箭头函数不创建自身的执行上下文,因此箭头函数中的 this 继承自其外层函数的词法环境。

2. 普通函数中的 this 默认指向全局对象 window

在实际开发中,通常不希望函数执行上下文中的 this 默认指向全局对象,因为这会破坏数据边界并可能导致意外操作。若需让 this 指向特定对象,最佳实践是通过 call 方法显式指定。

另一种解决方案是启用 JavaScript 的严格模式(在代码第一行添加 "use strict";)。在严格模式下,默认调用一个函数时,其执行上下文中的 this 值为 undefined。

以上就是从 JavaScript 执行上下文视角详解 this 使用的详细内容,更多关于 JavaScript 执行上下文与 this 的资料请关注本站其它相关文章!

作者最新文章