【问题标题】:Is there a way to have lexical `this` in methods using the ES6 shorthand method notation?有没有办法在使用 ES6 速记方法符号的方法中使用词法“this”?
【发布时间】:2017-07-08 13:19:25
【问题描述】:

关于 SO 的第一个问题,我希望我没有重复任何内容;我看过 other questions 并认为我的与众不同足以值得询问。

基本上,有没有办法让使用速记表示法编写的方法的方法主体中的this 成为词法或绑定到特定值?

这样做的动机来自于我在实现iterator protocol 时想要使用ES6 方法简写,其中@@iterator 方法在调用时会返回一个迭代器对象。该迭代器对象必须有一个next() 方法,当调用它自己时会返回另一个对象。我希望在第二个对象中引用原始实例(在我的例子中,Graph)。

请注意,在下面的代码 sn-p 中,由于this 绑定,它没有按预期工作,next() 使用的是ES6 method definition shorthand

class Graph {
  constructor(initialNodes) {
    this.data = [...initialNodes];
  }

  [Symbol.iterator]() {
    let nextIndex = 0;
    // `this` binding is fine here, as expected
    return {
      // look at that beautiful shorthand
      next() {
        return nextIndex < this.data.length
          // `this` binding is not fine here
          ? { done: false, value: this.data[nextIndex++] }
          : { done: true };
      }
    };
  }
}

我知道在对象返回之上定义函数,然后将该函数分配为要返回的对象的属性,但我再次想知道是否可以使用方法简写语法。

将返回对象上方的next()定义为常规函数,然后在对象属性赋值中绑定:

class Graph {
  // constructor

  [Symbol.iterator]() {
    let nextIndex = 0;
    // I know you can do this
    const next = function() {
      return nextIndex < this.data.length
        ? { done: false, value: this.data[nextIndex++] }
        : { done: true };
    };
    return {
      next: next.bind(this)
    };
  }
}

将返回对象上方的next()定义为箭头函数意味着不需要bind,但仍然没有方法语法简写:

class Graph {
  // constructor

  [Symbol.iterator]() {
    let nextIndex = 0;
    // I know you can also do this
    const next = () => {
      return nextIndex < this.data.length
        ? { done: false, value: this.data[nextIndex++] }
        : { done: true };
    };
    return {
      next
    };
  }
}

Icepickle 在 cmets 中提出了一个很好的观点,我可以使用存储 this 上下文并在速记方法主体中引用它。我假设这是我能得到的最接近的?

class Graph {
  // constructor

  [Symbol.iterator]() {
    let nextIndex = 0;
    const self = this;
    return {
      next() {
        return nextIndex < self.data.length
          ? { done: false, value: self.data[nextIndex++] }
          : { done: true };
      }
    };
  }
}

【问题讨论】:

  • next: () =&gt; 并不比next() 长多少。我会同意的。
  • 箭头语法的重点是它继承了词法this,而不是从它的调用方式继承this。人们现在似乎只想使用箭头语法,并以某种方式使其完成原始语法所做的一切。这不是它的设计方式。如果您不打算使用词法this,那么您可能不应该使用箭头语法。使用更长的语法,然后您可以.bind() 满足您的需求。
  • @jfriend00:我认为他们要求的是相反的:词法this,但仍然使用速记方法符号。
  • @Ryan - 非常令人困惑的问题 - 只看标题。箭头语法通常不应该用于方法定义,因为您希望 this 来自它被称为 obj.method() 的方式。我想如果不是问题标题所说的,我就不会遵循他们的要求。我在这里放弃。
  • 对不起,我的问题令人困惑,@jfriend00,我不打算这样。 @Ryan 说得对,因为我想保留方法的简写但有词汇 this。我将尝试编辑我的问题以反映这一点。如果您不完全了解我来自哪里,我上面代码的问题方法是迭代器对象:当调用iterator.next() 时,我希望next() 中的this 引用@ 987654354@ 实例。也许那是低于标准的代码,尽管我几乎是从MDN 抄来的。

标签: javascript methods ecmascript-6 this bind


【解决方案1】:

解决您的问题的理想方法是使用适合该工作的工具:generator function。它们也可以与shorthand syntax 一起使用,并且它们将正确绑定this

生成器函数返回符合iterator protocol 的对象,并且可以使用yield* 将迭代器的输出委托给另一个可迭代对象(如数组)。

class Graph {
  constructor(initialNodes) {
    this.data = [...initialNodes];
  }

  * [Symbol.iterator]() {
    yield* this.data;
  }
}

const graph = new Graph([1, 2, 3]);
for (const node of graph) {
  console.log(node);
}

【讨论】:

  • 哇,谢谢!我没有考虑过生成器,因为我没有完全理解它们,但我想它们并不太难理解。这很酷,尤其是速记。我注意到在 MDN 速记生成器方法链接中,* 和生成器名称之间有一个空格,而在您的代码 sn-p 中没有。我查阅了 airbnb 的风格指南以了解他们对此的看法,发现他们实际上甚至不推荐使用迭代器或生成器。对此有什么想法吗? github.com/airbnb/javascript#iterators-and-generators
  • @abigsmall 他们不喜欢迭代器的原因是因为他们鼓励使用非函数式语法。如果您创建一些小的辅助函数,则可以以函数方式使用生成器。我个人觉得生成器语法超级强大,转换为数组就像[...graph] 一样简单。我不知道速记符号有标准样式,谢谢!
  • 感谢您的回答,@4castle :)
猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 2013-09-20
  • 2013-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多