"this" 关键字如何与 JavaScript 中的对象一起使用?

How does the "this" keyword work with objects in JavaScript?

由于我试图掌握 JavaScript 中可迭代对象的概念,我使用以下代码构建了一个:

let range = {
  from: 0,
  to: 5
};

range[Symbol.iterator] = function() {
  return {
    current: this.from,
    last: this.to,

    next() {
      if (this.current <= this.last) {
        return { done: false, value: this.current++ };
      } else {
        return { done: true };
      }
    }
  };
};

for (let num of range) {
  console.log(num);
}

Symbol.iterator 方法 returns 包含一个方法 (next) 和两个属性(currentfrom)的对象。此对象内的属性等于对象内的属性 range.

因为 current 等于 this.fromlast 等于 this.to,我假设我可以直接从内部使用 this.fromthis.to方法 next 如下:

let range = {
  from: 0,
  to: 5
};

range[Symbol.iterator] = function() {
  return {
    next() {
      if (this.from <= this.to) {
        return { done: false, value: this.from++ };
      } else {
        return { done: true };
      }
    }
  };
};

for (let num of range) {
  console.log(num);
}

但是,这样做会阻止迭代开始。观察到这一点后,我有两个问题:

  1. 为什么可以属性currentlast使用关键字this并让它引用对象range 而方法 next 不能? currentlastnext() 都是 相同的 对象的一部分,由 Symbol.iterator.[=42= 返回]

  2. 此外,鉴于 Symbol.iterator() returns 一个 单独的 对象, this 关键字不应该在那对象指的是那个对象本身?换句话说,属性 currentlast 不应该 能够使用关键字 [=28= 从 range 访问属性]? range不是一个单独的对象吗?

众所周知是一件棘手的事情。如此之多,以至于凯尔·辛普森 (Kyle Simpson) 几乎用整本书来讨论这个主题 (You Don't Know JS: this & Object Prototypes)。但是,在您的示例中,答案很简单 - 在第二个函数中,thisnext() 函数中,因此解析为此函数。

为了检查 this 的范围,我正在使用 Chrome 的调试工具。如果您在右行暂停,它会告诉您 this 解析为什么。

关键字this很复杂。当有更好的选择时避免使用它是明智的,通常会有,因为它有很多问题。

除此之外,答案如下:this 指的是 激活对象 (有时称为 上下文对象 ) 它的包含函数被调用。

激活对象是什么?它是调用函数时附加到的对象。 console.log() 的激活对象是 console.

然而...

const x = {
    log : console.log
};
x.log();

...这里x.log()console.log()完全一样,只是激活对象是x而不是console.

看起来很简单,对吧?是的,有点。但还有更多事情需要了解。

  • 有一个默认的上下文对象,当调用一个函数而不附加到一个对象时使用。它是全局对象,在浏览器中称为window,在Node.js中称为global
  • 如果脚本是 strict mode 中的 运行,则没有默认上下文对象,如果未使用上下文
  • 显式调用,它将是 undefined
  • Arrow functions 使用词法范围,它们的 this 值根本不是通常的上下文对象——它是箭头函数定义的任何地方的父函数的上下文对象

现在让我们将上面的内容应用到您的代码中。最重要的是,如果在上下文为 range 对象的情况下调用您的 next() 方法,它就会起作用。问题是,在引擎盖下,引擎基本上是这样做的...

const returnedObject = range[Symbol.iterator]();
returnedObject.next();

... 所以 next 是以 returnedObject 作为上下文调用的,而不是 range。但是返回对象 的函数是 range 作为其上下文调用的。因此,this每个地方都不一样。

您实际上可以通过使用箭头函数而不是 shorthand 方法非常轻松地解决您的问题。

这会起作用:

let range = {
  from: 0,
  to: 5
};

range[Symbol.iterator] = function() {
  return {
    next : () => {
      if (this.from <= this.to) {
        return { done: false, value: this.from++ };
      } else {
        return { done: true };
      }
    }
  };
};

for (let num of range) {
  console.log(num);
}