Is there a way to have lexical `this` in methods u

2020-04-23 07:10发布

First question on SO and I hope I’m not duplicating anything; I’ve looked at other questions and think mine is different enough to warrant asking.

Basically, is there a way to have the this that’s in the method body of a method written using the shorthand notation to be either lexical or bound to a specific value?

The motivation for this comes from wanting to use the ES6 method shorthand when I was implementing the iterator protocol, in which the @@iterator method, when called, returns an iterator object. That iterator object has to have a next() method, which when called itself returns another object. It’s in this 2nd object that I want a reference to the original (in my case, Graph) instance.

Note that in the below code snippet, which does not work as intended due to the this binding, next() is using the 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 };
      }
    };
  }
}

I’m aware of defining the function above the object return and then assigning that function as a property of the object being returned, but again I was wondering if a way with the method shorthand syntax was possible.

Defining next() above the return object as a regular function then binding it in the object property assignment:

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

Defining next() above the return object as an arrow function means no need for bind, but still no method syntax shorthand:

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 brought up a good point in the comments that I could just use store the this context and refer to that in the shorthand method body. I’m assuming this is the closest I can get?

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

1条回答
看我几分像从前
2楼-- · 2020-04-23 07:31

The ideal solution to your problem would be to use the proper tool for the job: a generator function. They too can be used with a shorthand syntax, and they will properly bind this.

Generator functions return objects which conform to the iterator protocol, and can use yield* to delegate the output of the iterator to another iterable object (like an array).

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

查看更多
登录 后发表回答