【问题标题】:addEventListener, arrow functions, and `this` [duplicate]addEventListener、箭头函数和“this”[重复]
【发布时间】:2017-12-06 01:36:35
【问题描述】:

我正在经历 JavaScript30 挑战,在 lesson 3 中,他有一些事件侦听器调用一个函数,该函数引用它作为 this 调用的元素:

const inputs = document.querySelectorAll('.controls input');
function handleUpdate() {
  const suffix = this.dataset.sizing || '';
  document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix);
}
inputs.forEach(input => input.addEventListener('change', handleUpdate));
inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));

我试图用 ES6 箭头函数重写它,但我无法让 this 正常工作。我使用target 得到了解决方法:

const handleUpdate = (e) => {
  const that = e.target;
  const newValue = `${that.value}${that.dataset.sizing || ''}`;
  etc.
}

但我首先尝试像这样绑定函数:

input.addEventListener('change', handleUpdate.bind(this));

但是函数里面的this仍然指向window,我不明白为什么。

在这种情况下,是否没有将函数绑定到元素的“正确”方法?

【问题讨论】:

  • 如果你想使用this,那么你需要使用常规函数。箭头函数在this 中不能正常工作。
  • 另外,有什么正确的方法可以用箭头函数来做到这一点吗?
  • 我想我记得读过一些东西,说或建议,Arrow 函数是明确编写的,以避免影响“this”。
  • 正常功能和thise.currentTarget(不是e.target!!)
  • 箭头函数只允许从它们定义的环境中继承“this”的值,而不是它们被调用的上下文。演示代码使用常规函数,“this”引用它被调用的环境。根据定义,箭头函数是不可能的,因此为什么不能在任何需要使用“this”的地方使用它们。

标签: javascript this


【解决方案1】:

这是什么?

this是Javascript中的一个特殊关键字,指的是函数的执行环境:

  • 如果在全局范围内执行一个函数,this将被绑定到窗口
  • 如果将函数传递给事件处理程序的回调,this 将绑定到引发事件的 DOM 元素

捆绑

bind 方法基本上说,当你调用函数时,用我的参数替换它。所以,例如:

let a = {}
function test_this() {
     return this === a;
}  

test_this(); // false
test_this.bind(a)(); // true (.bind() returns the bound function so we need to call the bound function to see the result)

此外,箭头函数只是语法糖,用于将函数的 this 绑定到 this 的当前值。例如,

let b = () => { /* stuff */ }

是相同的

let b = (function () { /* stuff */}).bind(this);

(基本上,我知道这是过于简单化了)

你的困境

在正常的事件过程中(不使用箭头函数),this 被绑定到 DOM 元素。

当您执行事件处理程序 input.addEventListener('change', handleUpdate.bind(this)); 的创建时,您正在全局范围内运行(因此 this === window)。所以你有效地运行input.addEventListener('change', handleUpdate.bind(window));(这是你注意到的行为)。和使用箭头函数是一样的。

如果你想用匿名函数替换回调,你应该这样做:

const handleUpdate = function (e) {
  const that = e.target;
  const newValue = `${that.value}${that.dataset.sizing || ''}`;
  // etc.
}

【讨论】:

  • 箭头函数没有this 的概念。箭头函数内部的 this 是它们包含的词法环境中的任何 this
  • 因此,“我知道这是过于简单化了”。我认为在某人刚刚学习 JS 时尝试讨论词法范围可能不是最好的教学方法。如果您正在阅读本文并想要更正确的解释:箭头函数不绑定 this 所以 this 只是指它在封闭的词法范围内所指的任何东西。但是,如果他们只是将 this 绑定到封闭范围,这在功能上是相同的。
  • 关键是您的整个答案可以替换为“箭头函数没有 this 的概念。箭头函数内部的 this 是它们包含的词法环境中的任何内容。如果您需要绑定的值,请不要使用箭头函数this。”以及可能对 ECMAScript 2015 语言规范的一些引用。
  • 这无法解释为什么他对 .bind 的尝试没有奏效
  • 这绝对可以解释为什么他的绑定尝试不起作用“箭头函数没有 this 的概念。箭头函数内部的 this 是它们包含的词法环境中的任何内容。如果需要绑定,请不要使用箭头函数这个的价值。”