在 forEach 中调用 class 函数:Javascript 如何处理 "this" 关键字

Calling a class function in forEach: how Javascript handles "this" keyword

我是 Javascript 的新手,只是想确保我了解它如何处理 this 关键字,因为...好吧,它看起来很乱。我已经在 Whosebug 上查看了类似的问题,并想确保我没有提出错误的想法。

所以我像这样定义了一个 class,并希望处理构造函数中接收到的每个点。

function Curve(ptList) {
  this.pts = [];

  if(ptList.length > 2) {
    // I want to call "addPoint" for every item in ptList right here
  }
}

Curve.prototype.addPoint = function(p) { 
  this.pts.push(p);
  /* some more processing here */ 
}

所以,最初我认为我可以这样做:

ptList.forEach(this.addPoint);

但我不能,因为那只是传递一个指向原型函数的指针,这意味着 addPoint 中的 this 指的是全局对象。

然后我尝试了:

ptList.forEach(function(p) { this.addPoint(p); });

但是我不能,因为this一进入那个内部函数就指的是全局作用域(它不再指正在构造的Curve对象),所以addPoint 未定义。

解决这个问题的方法是在我仍然可以在子函数中与之交谈的范围内创建一个引用 this 的变量:

var _this = this;
ptList.forEach(function(p) { _this.addPoint(p); });

这终于奏效了(但对于没有 JS 经验的人来说看起来真的很奇怪)。但后来我发现了 bind 函数,它让我无法定义一个简单的函数包装器:

ptList.forEach(this.addPoint.bind(this));

最后,这似乎是最好、最简洁的方法,即使它看起来很傻。没有 bind 函数,addPoint 就不知道调用了哪个对象,没有第一个 this,我什至找不到我的 addPoint 函数。

有没有更好的方法来做这个看似简单的事情,或者最后一行代码是推荐的方法吗?

forEach() 方法和其他类似的数组方法如 map()filter() 提供了一个名为 thisArg 的可选参数,专门用于此目的。

这作为调用函数时提供给函数的this "argument":

ptList.forEach(this.addPoint, this);

当这样的参数不可用时,选择使用闭包变量(变量名 self 通常用于此)还是 .bind() 主要取决于偏好。请注意,另一种选择是在构造函数中定义函数并在闭包中捕获所有内容:

function Curve(ptList) {
  var pts = [];
  function addPoint(p) {
      pts.push(p);
  }

  if(ptList.length > 2) {
    ptList.forEach(addPoint);
  }

  this.addPoint = addPoint;
}

这比将函数放在原型上需要更多的内存,因为每个实例都有自己的副本(并且还排除了原型允许你做的一些事情),但是道格拉斯克罗克福德([的发明者=31=] 和 JSLint) 最近说他甚至不再使用 this,理由是内存很便宜,但 CPU 周期不是(遍历原型链需要相当多的处理)。

通常的做法是 var that = this。 JavaScript 中的继承起初看起来很奇怪,但您应该坚持使用常用的。做类似 ptList.forEach(this.addPoint.bind(this)) 的事情会使下一个使用您的代码的人感到困惑。

What does 'var that = this;' mean in JavaScript?