addEventListener,箭头函数和`this`

9

我正在进行JavaScript30挑战,在第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 协同工作。 - slebetman
1
还有,使用箭头函数有没有正确的方法来做这件事?没有 - slebetman
我记得读过一些内容,说或建议箭头函数是专门编写的,以避免影响“this”。 - David Thomas
1
要么使用普通函数和this,要么使用e.currentTarget(不是e.target!!) - Oriol
箭头函数只能从定义它们的环境中继承'this'的值,而不能从调用它们的上下文中继承。演示代码使用了一个普通函数,'this'引用了它被调用的环境。这在箭头函数中是不可能的,因此它们不能在任何需要使用'this'的地方使用。 - Rich
1个回答

10

这是什么?

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

  • 如果你在全局作用域中执行一个函数,this 将绑定到 window 对象
  • 如果你将函数传递给事件处理程序的回调函数,this 将绑定到引发事件的 DOM 元素

绑定

bind方法基本上是说,当你调用这个函数时,使用我的参数替换 this。例如:

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

4
箭头函数没有“this”概念。箭头函数内部的“this”是其所在词法环境中的“this”。需要注意不改变原意,尽可能地使翻译通俗易懂。 - user4639281
1
因此"我知道这只是一个过于简化的说法"。我认为当有人刚开始学习JS时,试图讨论词法作用域可能不是最好的教学方法。如果你正在阅读这里并想要更正确的解释:箭头函数不会绑定this,所以this只是指向了它在封闭的词法作用域中所指向的东西。然而,从功能上来说,这与将this绑定到封闭作用域是相同的。 - Tomas Reimers
1
重点是你的整个答案可以被替换为“箭头函数没有this的概念。箭头函数内部的this是其包含词法环境中的任何this。如果需要绑定this的值,请勿使用箭头函数。”,可能还包括对ECMAScript 2015语言规范的一些引用。 - user4639281
这绝对可以解释为什么他尝试绑定时失败了:“箭头函数没有this的概念。箭头函数内部的this是其包含词法环境中的this。如果需要绑定this的值,请勿使用箭头函数。” - user4639281
我指的是 input.addEventListener('change', handleUpdate.bind(this)); - Tomas Reimers
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接