首页 > web前端 > js教程 > 正文

如何理解JavaScript中的this关键字?

紅蓮之龍
发布: 2025-09-19 17:10:01
原创
823人浏览过
this的指向取决于函数调用方式,其规则按优先级分为:箭头函数继承外层作用域this;new绑定指向新实例;显式绑定(call/apply/bind)指定this值;隐式绑定指向调用对象;默认绑定指向全局或undefined。

如何理解javascript中的this关键字?

JavaScript中的

this
登录后复制
关键字,说白了,它就是一个函数在执行时,指向的那个“上下文对象”。它的值不是固定不变的,而是完全取决于函数被调用的方式。理解这一点,是掌握
this
登录后复制
的关键。

解决方案

要深入理解

this
登录后复制
,我们需要看清它在不同调用模式下的表现。这就像是
this
登录后复制
有几张不同的面孔,每次函数被调用,它就根据当前的面具来决定自己是谁。

最常见的几种绑定规则包括:

  1. 默认绑定 (Default Binding): 当函数作为独立函数被调用,没有明确的上下文对象时,

    this
    登录后复制
    会指向全局对象(在浏览器中是
    window
    登录后复制
    ,在Node.js中是
    global
    登录后复制
    )。但在严格模式下,
    this
    登录后复制
    会是
    undefined
    登录后复制
    ,这通常是为了防止意外地修改全局对象。

    立即学习Java免费学习笔记(深入)”;

    function showThis() {
      console.log(this);
    }
    
    showThis(); // 在浏览器中输出 Window 对象,在严格模式下输出 undefined
    登录后复制
  2. 隐式绑定 (Implicit Binding): 当函数作为对象的方法被调用时,

    this
    登录后复制
    会指向调用它的那个对象。这是我们最常遇到的情况。

    const person = {
      name: 'Alice',
      greet: function() {
        console.log(`Hello, my name is ${this.name}`);
      }
    };
    
    person.greet(); // 输出 "Hello, my name is Alice",因为 greet 是通过 person 对象调用的
    登录后复制

    这里有个常见的陷阱:如果把

    person.greet
    登录后复制
    赋值给一个变量,再通过变量调用,就会失去隐式绑定,退化为默认绑定。

    const sayHello = person.greet;
    sayHello(); // 在非严格模式下输出 "Hello, my name is undefined" (或指向全局对象的 name),因为此时 greet 失去了 person 的上下文
    登录后复制
  3. 显式绑定 (Explicit Binding): 我们可以强制改变

    this
    登录后复制
    的指向,使用
    call()
    登录后复制
    ,
    apply()
    登录后复制
    ,
    bind()
    登录后复制
    这三个方法。

    • call()
      登录后复制
      apply()
      登录后复制
      会立即执行函数,并接受第一个参数作为
      this
      登录后复制
      的值。
      call()
      登录后复制
      接受参数列表,
      apply()
      登录后复制
      接受参数数组。
    • bind()
      登录后复制
      则会创建一个新函数,这个新函数的
      this
      登录后复制
      永远被绑定到
      bind()
      登录后复制
      的第一个参数上,它不会立即执行。
    function introduce(age, city) {
      console.log(`I'm ${this.name}, ${age} years old, from ${city}.`);
    }
    
    const user = { name: 'Bob' };
    
    introduce.call(user, 30, 'New York'); // I'm Bob, 30 years old, from New York.
    introduce.apply(user, [25, 'London']); // I'm Bob, 25 years old, from London.
    
    const boundIntroduce = introduce.bind(user, 40);
    boundIntroduce('Paris'); // I'm Bob, 40 years old, from Paris. (注意这里 bind 也可以预设部分参数)
    登录后复制
  4. new
    登录后复制
    绑定 (New Binding): 当函数作为构造函数,使用
    new
    登录后复制
    关键字调用时,
    this
    登录后复制
    会指向新创建的实例对象。

    function Car(make, model) {
      this.make = make;
      this.model = model;
    }
    
    const myCar = new Car('Honda', 'Civic');
    console.log(myCar.make); // Honda
    console.log(myCar.model); // Civic
    登录后复制

    在这个场景下,

    this
    登录后复制
    指向的是
    new
    登录后复制
    操作符创建的那个空对象,然后构造函数会往这个空对象上添加属性。

  5. 箭头函数 (Arrow Functions): 这是ES6引入的,它彻底改变了

    this
    登录后复制
    的绑定方式。箭头函数没有自己的
    this
    登录后复制
    ,它会捕获其所在词法作用域(即定义时所处的外部作用域)的
    this
    登录后复制
    值。一旦确定,
    this
    登录后复制
    就不会再改变。

    const person = {
      name: 'Charlie',
      sayLater: function() {
        setTimeout(function() {
          // 这里的 this 默认指向全局对象 (Window),因为 setTimeout 的回调函数是独立调用的
          console.log(`Regular function: ${this.name}`); // undefined 或全局对象的 name
        }, 100);
      },
      sayLaterArrow: function() {
        setTimeout(() => {
          // 箭头函数捕获了 sayLaterArrow 定义时的 this,也就是 person 对象
          console.log(`Arrow function: ${this.name}`); // Charlie
        }, 100);
      }
    };
    
    person.sayLater();
    person.sayLaterArrow();
    登录后复制

    箭头函数在处理回调函数时尤其方便,因为它避免了

    this
    登录后复制
    上下文丢失的问题,省去了我们手动
    bind
    登录后复制
    _this = this
    登录后复制
    的麻烦。

为什么JavaScript中的this总是让人困惑不解?

我记得刚接触JavaScript时,

this
登录后复制
简直就是个谜团。它不像Java或C++那样,
this
登录后复制
总是指向当前实例。JavaScript的
this
登录后复制
更像一个变色龙,它的颜色(指向)完全取决于它被“放在”哪个环境里(如何被调用)。这种动态性是其困惑的根源。

首先,多重绑定规则是主要原因。我们有默认绑定、隐式绑定、显式绑定、

new
登录后复制
绑定,以及后来出现的箭头函数带来的词法绑定。这些规则之间存在优先级,比如显式绑定通常高于隐式绑定,
new
登录后复制
绑定又高于显式绑定。而箭头函数则完全不走寻常路,它直接跳过了所有这些规则,去父级作用域找
this
登录后复制
。这种复杂的优先级和不同寻常的绑定机制,让开发者很难形成一个统一的心智模型。

其次,回调函数的上下文丢失是另一个常见的痛点。在异步操作(如

setTimeout
登录后复制
、事件监听器)或数组方法(如
forEach
登录后复制
map
登录后复制
)中,如果直接传入一个包含
this
登录后复制
的普通函数作为回调,
this
登录后复制
往往会退化为默认绑定,指向全局对象(或
undefined
登录后复制
),而不是我们期望的那个对象。这导致很多初学者不得不使用
const self = this
登录后复制
或者
.bind(this)
登录后复制
来“固定”
this
登录后复制
,这本身就说明了其复杂性。

再者,严格模式的影响也增加了理解难度。在非严格模式下,默认绑定会指向全局对象,这有时会掩盖问题。但在严格模式下,默认绑定会是

undefined
登录后复制
,这会让错误暴露得更早,但也可能让不熟悉规则的开发者感到更困惑。

对我来说,真正理解

this
登录后复制
,是从放弃“
this
登录后复制
是一个固定指针”的观念开始的。它更像是一个“运行时上下文引用”,每次函数执行前,JavaScript引擎都会计算出它应该指向谁。

如何在不同场景下正确判断this的指向?

判断

this
登录后复制
的指向,其实可以遵循一个相对清晰的“决策树”或者说“优先级列表”。当你看到一个函数调用时,可以这样一步步地问自己:

微信源码微趣能Weiqn
微信源码微趣能Weiqn

产品介绍微趣能 Weiqn 开源免费的微信公共账号接口系统。MVC框架框架结构清晰、易维护、模块化、扩展性好,性能稳定强大核心-梦有多大核心就有多大,轻松应对各种场景!微趣能系统 以关键字应答为中心 与内容素材库 文本 如图片 语音 视频和应用各类信息整体汇集并且与第三方应用完美结合,强大的前后台管理;人性化的界面设计。开放API接口-灵活多动的API,万名开发者召集中。Weiqn 系统开发者AP

微信源码微趣能Weiqn 1
查看详情 微信源码微趣能Weiqn
  1. 是不是箭头函数?

    • 如果是,那么
      this
      登录后复制
      不是由调用方式决定的。它会向上寻找定义它的那个最近的非箭头函数作用域(或者全局作用域),并继承那个作用域的
      this
      登录后复制
      。一旦确定,永不改变。
    • 如果不是,继续下一步。
  2. 是不是通过

    new
    登录后复制
    关键字调用的?

    • 如果是(例如
      new MyConstructor()
      登录后复制
      ),那么
      this
      登录后复制
      会指向新创建的那个对象实例
    • 如果不是,继续下一步。
  3. 是不是通过

    call()
    登录后复制
    apply()
    登录后复制
    bind()
    登录后复制
    显式调用的?

    • 如果是(例如
      myFunction.call(someObject, ...)
      登录后复制
      ),那么
      this
      登录后复制
      会指向传入的第一个参数
      someObject
      登录后复制
      )。如果传入
      null
      登录后复制
      undefined
      登录后复制
      ,则会退化为默认绑定(指向全局对象或
      undefined
      登录后复制
      )。
    • 如果不是,继续下一步。
  4. 是不是作为对象的方法调用的?

    • 如果是(例如
      myObject.myMethod()
      登录后复制
      ),那么
      this
      登录后复制
      会指向调用这个方法的那个对象
      myObject
      登录后复制
      )。注意,这里只看
      .
      登录后复制
      前面的那个对象。
    • 如果不是,继续下一步。
  5. 默认绑定:

    • 如果以上都不是,那么就是默认绑定。在非严格模式下,
      this
      登录后复制
      指向全局对象(浏览器中的
      window
      登录后复制
      ,Node.js中的
      global
      登录后复制
      )。在严格模式下,
      this
      登录后复制
      undefined
      登录后复制

这个流程图能帮助我们覆盖绝大多数情况。举个例子:

const obj = {
  name: 'Tester',
  method: function() {
    console.log(this.name);
  },
  arrowMethod: () => {
    console.log(this.name); // 这里的 this 捕获的是 obj 定义时的全局 this,所以是 undefined 或 Window.name
  }
};

obj.method(); // 步骤4:隐式绑定,this 指向 obj -> 'Tester'

const func = obj.method;
func(); // 步骤5:默认绑定,this 指向全局对象 -> undefined 或 Window.name

const anotherObj = { name: 'Another' };
obj.method.call(anotherObj); // 步骤3:显式绑定,this 指向 anotherObj -> 'Another'

obj.arrowMethod(); // 步骤1:箭头函数,this 捕获定义时的全局 this -> undefined 或 Window.name
登录后复制

通过这种逐层判断,你可以更系统地分析

this
登录后复制
的指向。

箭头函数如何改变了this的工作方式?

箭头函数是ES6带来的一项革命性特性,它在

this
登录后复制
的处理上采取了一种全新的策略:词法绑定。这意味着箭头函数没有自己的
this
登录后复制
,它的
this
登录后复制
值完全取决于它被定义时所处的外部(词法)作用域。一旦定义,
this
登录后复制
的指向就固定了,不会因为函数后续的调用方式而改变。

这与传统的

function
登录后复制
关键字定义的函数形成了鲜明对比。普通函数在执行时,
this
登录后复制
是动态绑定的,它的值由函数被调用的方式决定。而箭头函数则像一个“旁观者”,它不参与
this
登录后复制
的绑定过程,只是“借用”了父级作用域的
this
登录后复制

核心改变:

  1. 解决了回调函数中的

    this
    登录后复制
    丢失问题: 这是箭头函数最显著的优势之一。在ES5及之前,我们经常需要在异步回调(如
    setTimeout
    登录后复制
    )、事件处理器或数组迭代方法中,手动保存
    this
    登录后复制
    的引用(例如
    var self = this;
    登录后复制
    ),或者使用
    bind()
    登录后复制
    方法来确保
    this
    登录后复制
    指向正确。箭头函数通过词法绑定,自然而然地解决了这个问题。

    class Greeter {
      constructor(name) {
        this.name = name;
      }
    
      // 传统函数,this 在 setTimeout 回调中会丢失
      greetOld() {
        setTimeout(function() {
          console.log(`Hello from old way, ${this.name}`); // this 指向 Window/undefined
        }, 100);
      }
    
      // 箭头函数,this 捕获了 greetNew 方法定义时的 this (即 Greeter 实例)
      greetNew() {
        setTimeout(() => {
          console.log(`Hello from new way, ${this.name}`); // this 指向 Greeter 实例
        }, 100);
      }
    
      // 也可以直接将类方法定义为箭头函数,这样它的 this 总是指向实例
      greetMethod = () => {
        console.log(`Hello from arrow method, ${this.name}`);
      }
    }
    
    const myGreeter = new Greeter('World');
    myGreeter.greetOld(); // 输出 "Hello from old way, undefined"
    myGreeter.greetNew(); // 输出 "Hello from new way, World"
    myGreeter.greetMethod(); // 输出 "Hello from arrow method, World"
    setTimeout(myGreeter.greetMethod, 200); // 即使作为回调,this 也保持不变
    登录后复制
  2. 更简洁的代码: 避免了冗余的

    bind
    登录后复制
    调用或
    self = this
    登录后复制
    的赋值,代码更加简洁易读。

  3. 不能作为构造函数: 箭头函数不能使用

    new
    登录后复制
    关键字调用,因为它没有自己的
    this
    登录后复制
    ,也就无法为新对象绑定
    this
    登录后复制

  4. 没有

    arguments
    登录后复制
    对象: 箭头函数也没有自己的
    arguments
    登录后复制
    对象,它会从父级作用域继承
    arguments
    登录后复制

  5. 不适用于定义对象方法: 如果你希望对象方法中的

    this
    登录后复制
    指向该对象本身,那么使用普通函数定义方法是更合适的。如果使用箭头函数,
    this
    登录后复制
    会指向对象定义时所处的外部作用域(通常是全局对象),这可能不是你想要的。

    const myObject = {
      value: 42,
      getValue: function() {
        return this.value; // this 指向 myObject
      },
      getArrowValue: () => {
        return this.value; // this 指向全局对象(Window/undefined),而非 myObject
      }
    };
    
    console.log(myObject.getValue());      // 42
    console.log(myObject.getArrowValue()); // undefined (或 Window.value)
    登录后复制

总的来说,箭头函数提供了一种更可预测、更稳定的

this
登录后复制
行为,极大地简化了JavaScript中处理上下文绑定的复杂性,尤其是在涉及回调和高阶函数时。但理解它的工作原理,以及何时应该使用它,何时不应该,仍然是关键。

以上就是如何理解JavaScript中的this关键字?的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号