【问题标题】:confused again by javascript this再次被javascript混淆了
【发布时间】:2019-08-01 21:00:56
【问题描述】:

一些更新: 感谢大家的帮助。我想这可能是关键的混淆点:参数区域中的“this”不被视为函数“内部”,因此不会遵循 mdn 指定的规则(this 指向调用该方法的 obj)。下面的例子:

someObj {
    someF(//but if "this" shows up here, it doesn't point to someObj) {
        //when called, "this" here will point to someObj
    }
}

原始问题:

看了很多文档后,我以为我对此有很好的理解,但我错了。

下面的例子来自MDN

function Counter() {
  this.sum = 0;
  this.count = 0;
}
Counter.prototype.add = function(array) {
// Here "this" points to obj
  array.forEach(function(entry) {
    this.sum += entry;
    ++this.count;
  }, this);
  // ^---- Note, why it points to obj, not array [2,5,9]???

// Here "this" points to obj
};

const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 
obj.sum;
// 16

我明白了:

  • 需要将this传递给forEach,否则在回调函数中,this会指向全局/窗口(非严格模式)。
  • function(array) 的大部分区域内,this 指向 obj(从新的Counter() 创建),如 cmets 所示。
  • 回调函数如何使用从 forEach 传递的“this”作为第二个参数。我对此没有任何疑问

但基于此article on MDN 具体来说:

"作为对象方法,当函数作为对象的方法被调用时 对象,它的 this 设置为调用该方法的对象。”

不应该将this(由 ^---Note 突出显示)传递到指向数组对象的回调点中,即在这种情况下为 [2,5,9]。为什么会指向 obj 而不是数组?

非常感谢您的帮助,这太令人困惑了。

【问题讨论】:

  • forEach 不是对象的一部分。它在array 上调用,它与add 内部的this 指向Counter 实例不同。因此,要访问 Counter 实例变量,您需要传递 this,它将替换通常的 array this。如果你不在this 里面forEach 指向array
  • 谢谢,@GillesC。引用自 mdn “相同的概念适用于定义在对象原型链上某处的方法。如果该方法位于对象的原型链上,则 this 指的是调用该方法的对象,就好像该方法位于该对象上一样。”所以 forEach 在数组的原型链上,应该指向数组。是的,我们需要传递this,但是传入的“this”在array.forEach函数调用中,其中“this”应该已经表示[2,5,9],而不是obj
  • 感谢@HereticMonkey。我对如何在 forEach 的回调函数中使用第二个 this 毫无疑问。我的问题是,这里的方法(forEach)是在对象(array[2,5,9])上调用的,为什么其参数区的“this”指向obj,而不是array[2,5,9]
  • @GillesC 顺便说一句,如果我不将“this”作为 forEach 作为第二个参数传递,回调中的“this”将指向非严格模式下的窗口,而不是数组

标签: javascript foreach this


【解决方案1】:

需要在forEach中传入“this”,否则内部函数中的“this”会指向全局/窗口(非严格模式)。

this 的值取决于函数的调用方式。您看不到调用您传递给forEach 的回调函数的代码(它在浏览器内部)。碰巧,它确实是这样调用的,所以它是window(如果你没有将第二个参数传递给forEach)。

作为对象方法,当函数作为对象的方法被调用时,它的this被设置为调用该方法的对象

这无关紧要。您没有将回调函数作为对象的方法调用。您将其传递给 forEach ... 然后 forEach 正在调用它。

正在调用forEach作为数组的一个方法,所以forEach函数内——你看不到,因为它在内部browser — this 将是数组。

为什么它会指向 obj 而不是数组?

因为forEach 被明确设计为以这样一种方式调用回调函数,即forEach 的第二个参数是回调函数内的this 值。

你引用了这样说的文档。

【讨论】:

  • 实际上忘记了回调。我对 forEach 回调中的内容毫无疑问。我的问题是为什么传入的“this”不是[2,5,9]。 “this”出现在forEach的函数调用中,它位于对象数组的原型链上。那为什么它不指向数组。为什么它无关紧要?这实际上是我问题的关键。 MDN 明确表示 this 指向调用该方法的对象。这是什么对象:数组[2,5,9]。 “this”在哪里:在forEach方法参数区域内,由数组调用
  • @user3236895——你打电话给obj.add()而不是somearray.add()。这就是为什么评论说// Here "this" points to obj
【解决方案2】:

您可能会在以下将对象的this 的引用作为forEach() 的thisArg 传递时看到它更容易

Counter.prototype.add = function(array) {
  //here "this" points to obj
  const _self = this

  array.forEach(function(entry) {
    // _self passed in as second argument is now `this`
    console.log(this === _self) // true

    this.sum += entry;
    ++this.count;
  }, _self);
  // ^---- passing in reference to the `add` object instance `this`


};

【讨论】:

  • 我对这个实现没有任何问题,因为你将这个(obj)存储到_self并且你传递了_self,这很好。但是当你将“this”作为方法(在原型链上)传递时,它应该指向数组,而不是 obj。不应该吗?
【解决方案3】:

我认为您的困惑源于 MDN 引用中的“调用”一词,我不会说这是您的错。在obj.add([2, 5, 9]) 的情况下,obj.add 正在被“调用”[2, 5, 9],这听起来并没有错。然而,他们想说的是add 方法正在被“调用”obj

不管你想怎么表达它,它的工作方式(通常)是如果你从一个对象的属性调用一个函数,那个对象就是 this 将指向该函数内部的东西。对于obj.add([2, 5, 9])add 函数是通过对象obj 访问的,因此在add 的调用中将obj 设置为this

【讨论】:

  • 我不认为我误解了它。实际上,我正在应用相同的逻辑。 obj.add([2, 5, 9]): ***对象是什么? obj *** 这应该在 add 中指向什么? obj 然后相同的逻辑: array.forEach(......., this) ***什么是对象? array [2,5,9] ***这在 forEach 中应该指向什么?数组
  • 我使用相同的逻辑。告诉我这里的对象和方法是什么:array.forEach(.........., this);然后告诉我这里的“this”应该指向什么
  • @user3236895 如果你问这个this 是什么-> array.forEach(..., this) 然后看看我的回答。 this 指向 obj,因为那是 Counter.prototype.addthis。与forEachthis无关,只是传入forEach
  • @user3236895 只是为了让你知道我编辑了我的 cmets,我会在某个时候删除它。
【解决方案4】:

这个问题正是引入arrow functions 的原因。它们更紧密地绑定到您的封闭闭包。

function Counter() {
  this.sum = 0;
  this.count = 0;
}
Counter.prototype.add = function(array) {
  array.forEach(entry => {
    /* 
      Because we are using an arrow function here, it is using the 
      enclosing closure (which originates from `obj`).
    */
    this.sum += entry;
    ++this.count;
  });

};

const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.sum);
console.log(obj.count);

来自链接的文档:

箭头函数没有自己的 this。使用封闭词法范围的 this 值;箭头函数遵循正常的变量查找规则。因此,在搜索当前作用域中不存在的 this 时,箭头函数最终会从其封闭作用域中找到 this。

【讨论】:

  • 我知道箭头和传统函数的结果。我在问为什么。以箭头函数为例:“它正在使用立即关闭”。 “立即关闭”不应该是 array.forEach 而不是 obj.add 吗? array.forEach 中应该包含什么“this”?根据 mdn 的文档,“this”不应该指向数组,因为在“数组”上调用了 forEach?
  • 箭头函数没有自己的this,它是继承的。我提到的立即关闭是调用array.forEach 的位置,而不是出现this.sum 的位置。
  • 编辑答案以反映这一点。同样为了与文档保持一致,我将immediate 替换为enclosing
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 2020-11-17
  • 2013-02-02
  • 1970-01-01
  • 2012-10-06
相关资源
最近更新 更多