JavaScript 中的箭头函数和 `this` 函数:完整指南

  • 的价值 this 在 JavaScript 中,这取决于函数的调用方式,包括全局上下文、对象方法和严格模式的使用。
  • 箭头函数不会创建自己的 this相反,它们从词法上继承了定义它们的领域的术语,这避免了回调函数中的许多问题。
  • 建议在回调函数和数组方法中使用箭头函数,但应避免将其用作对象方法、构造函数或需要 DOM 事件处理程序的函数。 this 动态的。

JavaScript 中箭头和 this 函数的图示

如果你使用 JavaScript 编程已经有一段时间了,你肯定会认出这个关键字。 这让你头疼不止一次。自从 ES6 中出现了箭头函数之后,事情变得更加复杂……或者更简单,这取决于你如何看待它。

本文将深入探讨其工作原理。 这适用于传统函数和箭头函数。为什么有时它似乎指向特定对象,有时又指向全局对象?在什么情况下使用箭头函数是合理的,在什么情况下最好避免使用箭头函数?

究竟是什么 this 在 JavaScript 中

保留字 this 它指的是执行上下文 当前正在执行的函数。与其他语言不同,在 JavaScript 中,判断是否执行函数并非基于函数的定义位置,而是基于函数的执行情况。 如何调用.

这意味着同一个函数可以用不同的方式调用,并且在每一种调用方式中, this 可以指向不同的对象这不是你能通过直接指派来改变的事情(你不能这样做)。 this = algo但你可以通过一些特定机制来影响它,例如: call, apply y bind.

此外,它们的行为也各不相同。 严格模式和非严格模式在非严格模式下,如果您调用一个“裸”函数(前面没有对象), this 它通常是全局对象(在浏览器中, window),而在严格模式下,它可以是 undefined在比较来自不同来源的代码示例时,这种区别非常重要。

这在全球背景下以及正常功能中都是如此。

在浏览器中,当您不在任何模块或函数内部时,全局上下文就是对象。 window在那里 this 指向该对象也就是说,如果您在控制台中输入以下内容:

console.log(this === window); // true en un entorno de navegador no estricto

在以“经典”方式(普通函数)声明的函数中,其值为 this 这取决于该函数叫什么。如果在非严格模式下调用它而没有预先传入对象,则不会出现此问题。 this 它通常是全球性的,严格来说,它将是 undefined这就是为什么有时在将代码从一个站点迁移到另一个站点时, 这已经不是你所预期的了。.

这是用普通函数定义的对象方法。

当您使用传统语法在对象上定义方法时, this 在方法内部,引用对象本身 该方法正是从这里调用的。

例如,如果您有类似这样的内容:

const obj = {
  speak() {
    console.log(this);
  }
};
obj.speak();

电话 obj.speak() 使 thisspeak 成为那个人 obj这是人们通常凭直觉所期望的行为:该方法“代表”对象说话。

如果使用经典函数而不是简写语法,效果是一样的,因为 关键在于如何调用该方法。无论你使用方法缩写还是关键字,都无关紧要。 function 物体内部。

这是用箭头函数定义的方法。

当你使用箭头函数定义方法时,情况就有所不同了。例如:

const obj2 = {
  speak: () => {
    console.log(this);
  }
};
obj2.speak();

在这种情况下,执行 obj2.speak() 你会看到 this 它不再是 obj2但外部词汇语境 对于该对象,在经典的浏览器脚本中通常是全局对象。 window.

第一次看到这种情况会感到困惑,因为你通常会认为对象的方法应该指向对象本身。然而, 箭头函数不会创建自己的 this它们继承了以下价值 this 它们的作用域取决于其定义所在的范围。如果定义范围是全局的,则它们继承全局作用域;如果定义范围是其他的,则它们继承该其他作用域。

因此,现代文档中经常出现的一条建议是: 不要将箭头函数用作对象方法 当你需要的时候 this 瞄准那个物体。

词汇范围 this 箭头函数

普通函数和箭头函数的主要区别在于后者 具有词汇联系 this简而言之:他们不决定他们的 this 不是在他们互相打电话的时候,而是在他们 创建.

想象一下这个例子:

const obj3 = {
  speak() {
    (() => {
      console.log(this);
    })();
  }
};
obj3.speak();

在这里,似乎正如在……之内 speak 我们执行一个箭头函数, 这应该会“重置”到全局状态。但结果恰恰相反:箭头函数捕获了 this 它周围的函数在这种情况下,该方法 speak 被调用为 obj3.speak()因此,该值 this 控制台上显示的是来自 obj3.

我的意思是, 箭头函数没有自己的 this而是利用它们周围环境的资源。这在嵌套回调、定时器、Promise 以及其他任何使用传统函数时需要费力处理的地方都非常有用。 .bind 或者用一些技巧,比如 const that = this;.

丢失和保存的实际例子 this

JavaScript 中的一个经典问题是,当在方法内部定义函数时, 你失去了对……的引用 this 指向该物体 最终你会得到全球统一的版本,或者…… undefined.

让我们以使用情况的典型例子为例。 setTimeout 在具有传统函数的对象的方法中:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    setTimeout(function() {
      console.log(this.nombre);
    }, 3000);
  }
};
persona.decirNombre(); // Muestra undefined

这里 这是传递给函数的一部分 setTimeout 它不再是对象了 persona该回调函数在全局上下文中执行(在浏览器中, window), 所以 this.nombre 它试图读取全局变量中不存在的属性,结果失败了。 undefined.

在箭头函数出现之前,一种常见的解决方案是存储值 this 在辅助变量中 将其“拖”入函数中:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    let that = this; // aquí this es persona
    setTimeout(function() {
      console.log(that.nombre);
    }, 3000);
  }
};

多亏了这个变量,才能保持对对象的正确引用。但这是一种略显笨拙且重复的技巧。使用箭头函数,这个问题就大大简化了:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    setTimeout(() => {
      console.log(this.nombre);
    }, 3000);
  }
};

这里箭头函数不会创建自己的元素。 this所以 继承了 this 该方法 decirNombre即对象 persona结果:“Agustin”无需中间变量即可正确显示。 .bind.

调用、应用和绑定:控制值 this

除了使用方法调用这种“自然”的方式来设置上下文之外,JavaScript 还为我们提供了其他工具来…… 强制设置该值 this 在正常功能中: call, apply y bind.

方法 call() y apply() 它们会立即调用该函数,允许您传递要使用的对象。 this。 区别在于 call 逐一接收参数,同时 apply 它们以数组形式接收。 bind()相反,它会返回一个新函数。 this “附加”到您指定的值所以你可以稍后在方便的时候给她打电话。

然而,对于箭头函数,这些方法对于更改参数并不适用。 this 因为它的价值与词汇相关。 您可以使用 call, apply o bind 可以传递参数,但不能修改箭头函数的上下文,这与常规函数有一个非常重要的区别。

箭头函数的基本语法

超越行为 this箭头函数提供了一种 更简洁、更具表现力的语法 在许多情况下,通用形式为:

(arg1, arg2, ..., argN) => expresion

此表单会自动返回箭头右侧表达式的结果,因此 无需写出这个词 return 当你只有一个简单的表达式时。

一些常见的语法要点:

  • 无参数:
    () => 42 Øincluso _ => 42 如果你不在乎参数名称的话。
  • 仅需一个参数:
    括号是可选的;你可以这样写: x => x * 2 o (x) => x * 2.
  • 具有多个参数:
    括号是必需的: (x, y) => x + y.

当您需要多个语句时,可以使用 块体 带钥匙:

const sumar = (x, y) => {
  const resultado = x + y;
  return resultado;
};

在这种情况下,由于存在密钥, 不再存在任何隐式回报如果你不放 return该函数将返回 undefined这适用于箭头函数和传统函数。

使用箭头函数返回字面对象

有一个虽小但非常常见的语法细节:当箭头函数返回一个 字面对象直接必须将其用括号括起来,以免解释器将其误认为代码块。

例如:

x => ({ y: x })

如果没有这些圆括号,JavaScript 会将花括号解释为函数体的开头,而不是对象。这是一个简单的技巧,但如果你忘记了它,就会导致很多愚蠢的错误。

箭头函数:匿名且无需原型

箭头函数是 语法匿名它们没有正式名称,这可能会使事情变得有些复杂。 调试和错误消息因为在跟踪信息中,除非你将其分配给一个具有可识别名称的常量,否则你不会直接看到函数标识符。

此外,箭头函数 他们不拥有房产 prototype 它们不能用作建筑公司如果你试图用……召唤它们 new你会收到错误提示。要使用构造函数或类创建对象,仍然需要使用常规函数或语法。 class.

另一个后果是 它们不适用于需要内部自引用的模式。例如某些形式的递归或事件处理程序需要使用以下方式取消订阅: this 或者函数本身的名称。

箭头函数大放异彩

箭头函数的最大优势恰恰在于它们的 词汇链接 this在您希望传递给另一个函数的回调函数保持其原有特性时,它们是理想的选择。 this 周边地区。

例如,在一个包含启动计时器的方法的对象中,需要不断访问 使用对象本身的属性 this:

const contador = {
  id: 42,
  iniciar() {
    setTimeout(() => {
      console.log(this.id); // this es contador
    }, 1000);
  }
};

在 ES5 中,通常需要这样做。 .bind(this) 回调或保存 this 在另一个变量中。使用箭头函数时, 代码变得更简洁,更接近实际意图。.

它们在数组方法方面也非常实用,例如 map, filter, reduce 以及公司,因为 减少句法噪声 当函数逻辑简短时:

const numeros = [1, 2, 3];
const dobles = numeros.map(n => n * 2);

巧妙运用这些紧凑的形状,可以使数据流一目了然,更容易理解。

何时避免使用箭头函数

虽然箭头函数非常有用,但它们并不能替代常规函数。以下几个例子可以清楚地说明这一点: 最好不要使用它们。:

  • 依赖于对象方法的 this:
    如果你将方法定义为 saltos: () => { this.vidas--; } 物体内部 gato, this 它不会指向猫本身,而是指向外部环境,房产也不会像你预期的那样得到改善。
  • 需要 DOM 事件回调 this 动态:
    在类似这样的处理程序中 boton.addEventListener('click', () => { this.classList.toggle('on'); });, this 导致类型错误的可能不是按下的按钮,而是更高级别的上下文。
  • 需要构建器或函数 prototype:
    由于它不能与……一起使用 new箭头函数不适用于创建实例或基于原型的模式。

在所有这些情况下, 正常功能仍然是合适的工具 因为它允许这样做 this 它与你调用函数的方式动态关联。

如果你习惯于根据自身需求有意识地在普通功能和箭头功能之间进行选择,那么 this 根据上下文来看, 你的代码将更具可预测性和可读性。 供之后保存它的人使用。

最终,理解价值如何 this 在 JavaScript 中,箭头函数如何继承该值 要想不再为意外结果而苦恼,充分利用 ES6 的语法糖,并编写出完全符合你心意的方法、回调和事件处理程序,关键在于理解它们被创建的词法作用域。