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

this的指向取决于函数调用方式,其规则按优先级分为:箭头函数继承外层作用域this;new绑定指向新实例;显式绑定(call/apply/bind)指定this值;隐式绑定指向调用对象;默认绑定指向全局或undefined。

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

JavaScript中的

this

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

this

的关键。

解决方案

要深入理解

this

,我们需要看清它在不同调用模式下的表现。这就像是

this

有几张不同的面孔,每次函数被调用,它就根据当前的面具来决定自己是谁。

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

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

this

会指向全局对象(在浏览器中是

window

,在Node.js中是

global

)。但在严格模式下,

this

会是

undefined

,这通常是为了防止意外地修改全局对象。

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

function showThis() {  console.log(this);}showThis(); // 在浏览器中输出 Window 对象,在严格模式下输出 undefined

隐式绑定 (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 的上下文

显式绑定 (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 也可以预设部分参数)

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); // Hondaconsole.log(myCar.model); // Civic

在这个场景下,

this

指向的是

new

操作符创建的那个空对象,然后构造函数会往这个空对象上添加属性。

箭头函数 (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

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

是不是箭头函数?

如果是,那么

this

不是由调用方式决定的。它会向上寻找定义它的那个最近的非箭头函数作用域(或者全局作用域),并继承那个作用域的

this

。一旦确定,永不改变。如果不是,继续下一步。

是不是通过

new

关键字调用的?

如果是(例如

new MyConstructor()

),那么

this

会指向新创建的那个对象实例。如果不是,继续下一步。

是不是通过

call()

apply()

bind()

显式调用的?

如果是(例如

myFunction.call(someObject, ...)

),那么

this

会指向传入的第一个参数

someObject

)。如果传入

null

undefined

,则会退化为默认绑定(指向全局对象或

undefined

)。如果不是,继续下一步。

是不是作为对象的方法调用的?

如果是(例如

myObject.myMethod()

),那么

this

会指向调用这个方法的那个对象

myObject

)。注意,这里只看

.

前面的那个对象。如果不是,继续下一步。

默认绑定:

如果以上都不是,那么就是默认绑定。在非严格模式下,

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.nameconst 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

核心改变:

解决了回调函数中的

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 也保持不变

更简洁的代码: 避免了冗余的

bind

调用或

self = this

的赋值,代码更加简洁易读。

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

new

关键字调用,因为它没有自己的

this

,也就无法为新对象绑定

this

没有

arguments

对象: 箭头函数也没有自己的

arguments

对象,它会从父级作用域继承

arguments

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

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());      // 42console.log(myObject.getArrowValue()); // undefined (或 Window.value)

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

this

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1520868.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 13:42:30
下一篇 2025年12月20日 13:42:45

相关推荐

  • SASS 中的 Mixins

    mixin 是 css 预处理器提供的工具,虽然它们不是可以被理解的函数,但它们的主要用途是重用代码。 不止一次,我们需要创建多个类来执行相同的操作,但更改单个值,例如字体大小的多个类。 .fs-10 { font-size: 10px;}.fs-20 { font-size: 20px;}.fs-…

    2025年12月24日
    000
  • React 或 Vite 是否会自动加载 CSS?

    React 或 Vite 是否自动加载 CSS? 在 React 中,如果未显式导入 CSS,而页面却出现了 CSS 效果,这可能是以下原因造成的: 你使用的第三方组件库,例如 AntD,包含了自己的 CSS 样式。这些组件库在使用时会自动加载其 CSS 样式,无需显式导入。在你的代码示例中,cla…

    2025年12月24日
    000
  • React 和 Vite 如何处理 CSS 加载?

    React 或 Vite 是否会自动加载 CSS? 在 React 中,默认情况下,使用 CSS 模块化时,不会自动加载 CSS 文件。需要手动导入或使用 CSS-in-JS 等技术才能应用样式。然而,如果使用了第三方组件库,例如 Ant Design,其中包含 CSS 样式,则这些样式可能会自动加…

    2025年12月24日
    000
  • ElementUI el-table 子节点选中后为什么没有打勾?

    elementui el-table子节点选中后没有打勾? 当您在elementui的el-table中选择子节点时,但没有出现打勾效果,可能是以下原因造成的: 在 element-ui 版本 2.15.7 中存在这个问题,升级到最新版本 2.15.13 即可解决。 除此之外,请确保您遵循了以下步骤…

    2025年12月24日
    200
  • 您不需要 CSS 预处理器

    原生 css 在最近几个月/几年里取得了长足的进步。在这篇文章中,我将回顾人们使用 sass、less 和 stylus 等 css 预处理器的主要原因,并向您展示如何使用原生 css 完成这些相同的事情。 分隔文件 分离文件是人们使用预处理器的主要原因之一。尽管您已经能够将另一个文件导入到 css…

    2025年12月24日
    000
  • CSS 中如何正确使用 box-shadow 设置透明度阴影?

    css 中覆盖默认 box-shadow 样式时的报错问题 在尝试修改导航栏阴影时遇到报错,分析发现是 box-shadow 样式引起的问题。 问题原因 使用 !important 仍无法覆盖默认样式的原因在于,你使用了 rgb() 而不是 rgba(),这会导致语法错误。 立即学习“前端免费学习笔…

    2025年12月24日
    300
  • 为何scss中嵌套使用/*rtl:ignore*/无法被postcss-rtl插件识别?

    postcss-rtl插件为何不支持在scss中嵌套使用/*rtl:ignore*/ 在使用postcss-rtl插件时,如果希望对某个样式不进行转换,可以使用/*rtl:ignore*/在选择器前面进行声明。然而,当样式文件为scss格式时,该声明可能会失效,而写在css文件中则有效。 原因 po…

    2025年12月24日
    000
  • 构建模拟:从头开始的实时交易模拟器

    简介 嘿,开发社区!我很高兴分享我的业余项目 Simul8or – 一个实时日间交易模拟器,旨在为用户提供一个无风险的环境来练习交易策略。该项目 100% 构建在 ASP.NET WebForms、C#、JavaScript、CSS 和 SQL Server 技术堆栈上,没有外部库或框架。从头开始构…

    2025年12月24日
    300
  • Sass 中使用 rgba(var –color) 时的透明度问题如何解决?

    rgba(var –color)在 Sass 中无效的解决方法 在 Sass 中使用 rgba(var –color) 时遇到透明问题,可能是因为以下原因: 编译后的 CSS 代码 rgba($themeColor, 0.8) 在编译后会变为 rgba(var(–…

    2025年12月24日
    000
  • ## PostCSS vs. Sass/Less/Stylus:如何选择合适的 CSS 代码编译工具?

    PostCSS 与 Sass/Less/Stylus:CSS 代码编译转换中的异同 在 CSS 代码的编译转换领域,PostCSS 与 Sass/Less/Stylus 扮演着重要的角色,但它们的作用却存在细微差异。 区别 PostCSS 主要是一种 CSS 后处理器,它在 CSS 代码编译后进行处…

    2025年12月24日
    000
  • SCSS 简介:增强您的 CSS 工作流程

    在 web 开发中,当项目变得越来越复杂时,编写 css 可能会变得重复且具有挑战性。这就是 scss (sassy css) 的用武之地,它是一个强大的 css 预处理器。scss 带来了变量、嵌套、混合等功能,使开发人员能够编写更干净、更易于维护的代码。在这篇文章中,我们将深入探讨 scss 是…

    2025年12月24日
    000
  • 在 Sass 中使用 Mixin

    如果您正在深入研究前端开发世界,那么您很可能遇到过sass(语法很棒的样式表)。 sass 是一个强大的 css 预处理器,它通过提供变量、嵌套、函数和 mixins 等功能来增强您的 css 工作流程。在这些功能中,mixins 作为游戏规则改变者脱颖而出,允许您有效地重用代码并保持样式表的一致性…

    2025年12月24日
    200
  • SCSS:创建模块化 CSS

    介绍 近年来,css 预处理器的使用在 web 开发人员中显着增加。 scss (sassy css) 就是这样一种预处理器,它允许开发人员编写模块化且可维护的 css 代码。 scss 是 css 的扩展,添加了更多特性和功能,使其成为设计网站样式的强大工具。在本文中,我们将深入探讨使用 scss…

    2025年12月24日
    000
  • SCSS – 增强您的 CSS 工作流程

    在本文中,我们将探索 scss (sassy css),这是一个 css 预处理器,它通过允许变量、嵌套规则、mixins、函数等来扩展 css 的功能。 scss 使 css 的编写和维护变得更加容易,尤其是对于大型项目。 1.什么是scss? scss 是 sass(syntropically …

    2025年12月24日
    000
  • 如何正确使用 CSS:简洁高效样式的最佳实践

    层叠样式表 (css) 是 web 开发中的一项基本技术,允许设计人员和开发人员创建具有视觉吸引力和响应灵敏的网站。然而,如果没有正确使用,css 很快就会变得笨拙且难以维护。在本文中,我们将探索有效使用 css 的最佳实践,确保您的样式表保持干净、高效和可扩展。 什么是css? css(层叠样式表…

    2025年12月24日
    000
  • 花 $o 学习这些编程语言或免费

    → Python → JavaScript → Java → C# → 红宝石 → 斯威夫特 → 科特林 → C++ → PHP → 出发 → R → 打字稿 []https://x.com/e_opore/status/1811567830594388315?t=_j4nncuiy2wfbm7ic…

    2025年12月24日
    000
  • 深入理解CSS框架与JS之间的关系

    深入理解CSS框架与JS之间的关系 在现代web开发中,CSS框架和JavaScript (JS) 是两个常用的工具。CSS框架通过提供一系列样式和布局选项,可以帮助我们快速构建美观的网页。而JS则提供了一套功能强大的脚本语言,可以为网页添加交互和动态效果。本文将深入探讨CSS框架和JS之间的关系,…

    2025年12月24日
    000
  • 项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结

    项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结 随着互联网的快速发展,网页设计已经成为了各行各业都离不开的一项技能。优秀的网页设计可以给用户留下深刻的印象,提升用户体验,增加用户的黏性和转化率。而要做出优秀的网页设计,除了对美学的理解和创意的运用外,还需要掌握一些基本的技能,如…

    2025年12月24日
    200
  • 学完HTML和CSS之后我应该做什么?

    网页开发是一段漫长的旅程,但是掌握了HTML和CSS技能意味着你已经赢得了一半的战斗。这两种语言对于学习网页开发技能来说非常重要和基础。现在不可或缺的是下一个问题,学完HTML和CSS之后我该做什么呢? 对这些问题的答案可以分为2-3个部分,你可以继续练习你的HTML和CSS编码,然后了解在学习完H…

    2025年12月24日
    000
  • 聊聊怎么利用CSS实现波浪进度条效果

    本篇文章给大家分享css 高阶技巧,介绍一下如何使用css实现波浪进度条效果,希望对大家有所帮助! 本文是 CSS Houdini 之 CSS Painting API 系列第三篇。 现代 CSS 之高阶图片渐隐消失术现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 在上两篇中,我们…

    2025年12月24日 好文分享
    200

发表回复

登录后才能评论
关注微信