在forEach中调用类函数:Javascript如何处理"this".关键词 [英] Calling a class function in forEach: how Javascript handles "this" keyword

查看:132
本文介绍了在forEach中调用类函数:Javascript如何处理"this".关键词的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Java语言的新手,只是想确保我了解Javascript如何处理this关键字,因为...好吧,看起来好像很杂乱.我已经在StackOverflow上检查了类似的问题,并希望确保我不会提出错误的想法.

所以我要像这样定义一个类,并希望处理构造函数中收到的每个点.

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函数.

是否有更好的方法来执行此看似简单的操作,还是建议使用最后一行代码?

解决方案

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

与将函数放到原型上相比,这需要更多的内存,因为每个实例都有自己的副本(并且排除了原型允许您执行的某些操作),但是Douglas Crockford(JSON和JSLint的发明者) )最近表示,他不再使用this,理由是内存便宜,但CPU周期却不多(遍历原型链需要大量处理).

I'm new to Javascript and just want to make sure I'm understanding how it handles the this keyword, since... well, it seems like it's pretty messy. I've checked out similar questions on StackOverflow and want to make sure I'm not moving forward with the wrong idea.

So I'm defining a class like so, and want to process every point received in the constructor.

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 */ 
}

So, initially I thought I could just do:

ptList.forEach(this.addPoint);

but I can't, because that is simply passing a pointer to the prototype function, which means this in addPoint refers to the global object.

Then I tried:

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

but I can't, because this refers to the global scope as soon as I enter that internal function (it no longer refers to the Curve object being constructed), so addPoint is undefined.

The way to get around that is to make a variable that refers to this in a scope I can still talk to in the subfunction:

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

And this finally works (but looks really weird to someone without JS experience). But then I found out about the bind function, which lets me not define a trivial function wrapper:

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

and it seems like, finally, this is the best and most concise way to do it, even thought it looks silly. Without the bind function, addPoint has no understanding of which object it was called on, and without the first this, I can't even find my addPoint function.

Is there a better way to do this seemingly simple thing, or is that last line of code the recommended way?

解决方案

The forEach() method and other similar array methods like map() and filter() provide an optional parameter called thisArg specifically for this purpose.

This serves as the this "argument" that is provided to the function when it is called:

ptList.forEach(this.addPoint, this);

When such a parameter isn't available, then choosing between using a closure variable (the variable name self is often used for this) or .bind() is mostly a matter of preference. Note that another option is to define functions inside your constructor and capture everything in a closure:

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

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

  this.addPoint = addPoint;
}

This requires a bit more memory than putting the functions on a prototype since every instance gets its own copy (and also rules out some of the things a prototype allows you to do), but Douglas Crockford (the inventor of JSON and JSLint) recently said that he doesn't even use this anymore, with the rationale that memory is cheap, but CPU cycles are not (traversing the prototype chain requires a fair amount of processing).

这篇关于在forEach中调用类函数:Javascript如何处理"this".关键词的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆