Written in the beginning

  • ES6 common but ignored methods series of articles, sorting out the author thinks that some daily development may use some methods, use skills and some application scenarios, details please see related content links, welcome to supplement exchange.

Related articles

  • Common but overlooked methods for ES6 (Destruct assignments and values first)
  • ES6 Common but ignored methods (second bullet functions, arrays, and objects)
  • ES6 common but ignored methods (third bullet Symbol, Set, and Map)
  • ES6 commonly used but overlooked methods (fourth bullet Proxy and Reflect)
  • ES6 common but ignored methods (# 5 Promise and Iterator)
  • ES6 Common but ignored methods (async)
  • ES6 Common but ignored methods (eighth bullet Class)
  • ES6 common but ignored methods (Module 9)
  • Common but ignored methods of ES6 (Development specification of the Tenth Bullet Project)
  • ES6 common but ignored method (eleventh bullet Decorator)
  • Common but overlooked approaches to ES6 (End game – Latest Proposal)

Generator

  • ES6-Generator

The basic concept

  • GeneratorThe function isES6Provides an asynchronous programming solution with completely different syntactic behavior from traditional functions. In grammar,GeneratorA function is a state machine that encapsulates multiple internal states.performGeneratorThe function returns a traverser object that can be traversed in turnGeneratorEvery state inside the function.
  1. Characteristics of the
    1. functionThere is an asterisk between the keyword and the function name;
    2. Function body for internal useyieldExpression that defines different internal states.
    function* detanxGenerator() {
      yield 'detanx';
      return 'ending';
    }
    
    const dg = detanxGenerator();
    Copy the code
  2. call
    dg.next() // { value: 'detanx', done: false }
    dg.next() // { value: 'ending', done: true }
    dg.next() // { value: undefined, done: true }
    Copy the code
    1. The first call,GeneratorThe function starts executing until the first one is encounteredyieldExpression.nextMethod returns an object whosevalueThe property is the currentyieldValue of an expressionhello.doneThe value of the attributefalse, indicating that the traversal is not finished.
    2. The second call,GeneratorThe function executes from where the yield expression left off toreturnStatement (if notreturnStatement is executed until the end of the function.doneThe value of the attributetrueIs displayed, indicating that the traversal is complete.
    3. The third call, at this pointGeneratorThe function has run,nextMethod to return an objectvalueProperties forundefined.doneProperties fortrue. Call laternextMethod, which returns this value.
  3. writing
    • functionBetween keyword and function name*It’s not specified, so there are a lot of ways to write it, and it’s probably best to use the first one when we write it, which is*Followed byfunctionAfter the keyword,*Add another space after it.
    The function * foo (x, y) {...} function * foo (x, y) {...} function * foo (x, y) {...} function * foo (x, y) {...}Copy the code

Yield expression

  • GeneratorFunction returns the traverser object, only the callnextMethod iterates over the next internal state, so it actually provides a function that can pause execution.yieldThe expression is the pause flag.
  1. nextMethod run logic

    (1) EncounteryieldExpression, suspends subsequent operations and will immediately followyieldThe value of the following expression as the value of the returned objectvalueAttribute values.

    (2) Next callnextMethod, and continue until the next one is encounteredyieldExpression.

    (3) if there is no new encounteryieldExpression, and it runs until the end of the function, untilreturnStatement, and willreturnThe value of the expression following the statement as the value of the returned objectvalueAttribute values.

    (4) If the function does notreturnStatement of the object returnedvalueAttribute values forundefined.
  2. yieldExpression after an expression, only when callednextMethod, and an internal pointer to the statement. The following123 + 456Does not evaluate immediately, only when executednextTo the correspondingyieldThe expression is evaluated.
function* gen() {
  yield  123 + 456;
}
Copy the code
  1. yieldExpressions can only be used whenGeneratorFunction, if used anywhere else, will return an error.
(function (){
  yield 1;
})()
// SyntaxError: Unexpected number
Copy the code
  1. yieldIf an expression is used within another expression, it must be enclosed in parentheses. Use as a function argument or to the right of an assignment expression, without parentheses.
function* demo() { console.log('Hello' + yield); // SyntaxError console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK console.log('Hello' + (yield 123)); Function * demo() {foo(yield 'a', yield 'b'); // OK let input = yield; // OK }Copy the code

Interface with Iterator and for… Of cycles

  1. withIteratorInterface relationship
  • A bombES6 common but ignored methods (# 5 Promise and Iterator)I said, for any objectSymbol.iteratorMethod equal to the object’s traverser generator function, which returns an traverser object for the object.
  • GeneratorA function is a traverser generator function, and you can take theGeneratorAssigned to the objectSymbol.iteratorProperty so that the object hasIteratorInterface.
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};
[...myIterable] // [1, 2, 3]
Copy the code
  • GeneratorThe function returns an iterator object after execution. The object itself also hasSymbol.iteratorProperty that returns itself after execution.
function* gen(){
  // some code
}
var g = gen();
g[Symbol.iterator]() === g
// true
Copy the code
  1. for... ofcycle
  • for... ofLoops can be traversed automaticallyGeneratorGenerated when the function is runIteratorObject, and no call is required at this pointnextMethods. Once thenextMethod to return an objectdoneProperties fortrue.for... ofThe loop is terminated without the return object.
function* numbers() { yield 1; yield 2; return 3; } for (let v of numbers()) { console.log(v); } / / 1. 2Copy the code
  1. In addition tofor... ofcycle
  • Extended operator (.), destruct assignment andArray.fromMethod is called inside the traverser interface. They can both be convertedGeneratorFunction returnedIteratorObject as a parameter.
/ / extension operators [... Numbers ()] / [1, 2] / / Array from Array method. The from (Numbers ()) / / / / [1, 2] deconstruction assignment let (x, y) = Numbers (); x // 1 y // 2Copy the code

Parameters to the next method

  • yieldThe expression itself returns no value, or always returnsundefined.nextA method can take an argument that is treated asOn ayieldThe return value of an expression.
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
Copy the code
  • In the above code, the second time the next method is run without arguments, resulting in y equal to 2 * undefined (NaN), divided by 3 is NaN, so the value property of the returned object is also NaN. The third time the next method is run without arguments, so z equals undefined, and the value property of the returned object equals 5 + NaN + undefined, i.e. NaN.

  • If you supply parameters to the next method, the result is completely different. The first time the code above calls B’s next method, it returns the value 6 of x+1; The next method is called a second time, which sets the value of the last yield expression to 12, so y equals 24, and returns the value of 8 for y / 3; The third call to the next method sets the value of the last yield expression to 13, so z equals 13, where x equals 5 and y equals 24, so the return statement equals 42.

  • Due to thenextThe argument to the method represents the previous oneyieldThe return value of the expression, so used the first timenextMethod, passing parameters is invalid.

Next (), throw(), return()

In common
  • They both serve to makeGeneratorThe function resumes execution and is replaced with a different statementyieldExpression.
  1. next()Will be the last oneyieldThe expression is replaced by a value.
const g = function* (x, y) { let result = yield x + y; return result; }; const gen = g(1, 2); gen.next(); // Object {value: 3, done: false} gen.next(1); // let result = yield x + y;Copy the code
  1. throw()Is toyieldThe expression is replaced by onethrowStatements.
Gen. throw(new Error(' Error ')); // let result = yield x + y // let result = throw(new Error(' Error '));Copy the code
  1. return()Is toyieldThe expression is replaced by onereturnStatements.
gen.return(2); // let result = yield x + y // let result = return 2;Copy the code
The difference between
  1. Generator.prototype.throw()
    • GeneratorThe traverser object returned by the function,throwMethod can throw an error outside of a function, and then inGeneratorFunction capture in body.throwThe method can take an argument that is calledcatchStatement received, recommended to throwErrorObject.
    var g = function* () { try { yield; } catch (e) { console.log(e); }}; var i = g(); i.next(); I.row (new Error(' Error! ')); // Error: Error! (...).Copy the code
    • Do not confuse the traverser objectthrowMethod and globalthrowCommand. The error in the code above is with the traverser objectthrowMethod thrown instead of usingthrowCommand thrown.The latter can only be outside the functioncatchStatement capture.
    • ifGeneratorThere is no deployment inside the functiontry... catchCode block, sothrowMethod throws errors that will be externaltry... catchCode block capture.
    var g = function* () { while (true) { yield; Console. log(' internal capture ', e); }}; var i = g(); i.next(); try { i.throw('a'); i.throw('b'); } catch (e) {console.log(' external catch ', e); } // External capture aCopy the code
    • ifGeneratorThere is no deployment inside or outside the functiontry... catchCode block, then the program will report an error, directly interrupt execution.
    var gen = function* gen(){
      yield console.log('hello');
      yield console.log('world');
    }
    
    var g = gen();
    g.next();
    g.throw();
    // hello
    // Uncaught undefined
    Copy the code
    • throwMethods throw errors that are caught internally if they have been executed at least oncenextMethods. g.throw(1)When executed,nextMethod has not been executed once. Instead of being caught internally, the thrown error is thrown directly externally, causing the program to fail.
    function* gen() { try { yield 1; } catch (e) {console.log(' internal catch '); } } var g = gen(); g.throw(1); // Uncaught 1Copy the code
    • GeneratorErrors thrown outside the function can be caught inside the function. In turn,GeneratorErrors thrown inside a function can also be thrown outside a functioncatchCapture.
    function* foo() {
      var x = yield 3;
      var y = x.toUpperCase();
      yield y;
    }
    var it = foo();
    it.next(); // { value:3, done:false }
    try {
      it.next(42);
    } catch (err) {
      console.log(err);
    }
    Copy the code
    • The value is nonetoUpperCaseMethod, so one will be thrownTypeErrorError, outside of functioncatchCapture.
  2. Generator.prototype.return()
    • GeneratorThe traverser object returned by the function,returnMethod can return the given value and terminate the traversalGeneratorFunction.returnMethod called with no arguments, returns the valuevalueProperties forundefined.
    function* gen() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    var g = gen();
    
    g.next()        // { value: 1, done: false }
    g.return() // { value: undefined, done: true }
    Copy the code
    • ifGeneratorInside the function istry... finallyCode block, and executingtryCode block, soreturnMethod causes immediate entryfinallyCode block, after execution, the whole function will end.
    function* numbers () {
      yield 1;
      try {
        yield 2;
        yield 3;
      } finally {
        yield 4;
        yield 5;
      }
      yield 6;
    }
    var g = numbers();
    g.next() // { value: 1, done: false }
    g.next() // { value: 2, done: false }
    g.return(7) // { value: 4, done: false }
    g.next() // { value: 5, done: false }
    g.next() // { value: 7, done: true }
    Copy the code

yield*expression

  • yield*The expression is used in aGeneratorFunction to execute another oneGeneratorFunction.
function* foo() {
  yield 'a';
  yield 'b';
}
function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}
for (let v of bar()){
  console.log(v);
}
// "x"
// "a"
// "b"
// "y"
Copy the code
  • ifyield*This is followed by an array, which, since arrays natively support iterators, iterates through the group members.yieldIf the command is not followed by an asterisk, the entire array is returned, and an asterisk means that the array traverser object is returned.
function* gen(){
  yield* ["a", "b", "c"];
}
gen().next() // { value:"a", done:false }
Copy the code
  • Any data structure that hasIteratorInterface, can beyield*Traverse.
let read = (function* () { yield 'hello'; yield* 'hello'; }) (); read.next().value // "hello" read.next().value // "h"Copy the code
  • If the agentGeneratorFunction has areturnStatement, then you can send it to the proxyGeneratorFunction returns data. Function in the following examplefoothereturnStatement to functionbarThe return value is provided.
function* foo() {
  yield 2;
  yield 3;
  return "foo";
}

function* bar() {
  yield 1;
  var v = yield* foo();
  console.log("v: " + v);
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}
Copy the code

As an object propertyGeneratorFunction of writing

Let obj = {* myGeneratorMethod() {···}}; // let obj = {myGeneratorMethod: function* () {// let obj = {myGeneratorMethod: function* () {//Copy the code

GeneratorFunction of thethis

function* g() {} g.prototype.hello = function () { return 'hi! '; }; let obj = g(); obj instanceof g // true obj.hello() // 'hi! 'Copy the code
  • GeneratorfunctiongThe traverser returnedobj, it isg, and inheritsg.prototype. However, thegAs a normal constructor, it doesn’t work becausegThe traverser object is always returned instead ofthisObject.
  • GeneratorThe delta function can’t benewIf the commands are used together, an error message is displayed.
    function* F() {
      yield this.x = 2;
      yield this.y = 3;
    }
    
    new F()
    // TypeError: F is not a constructor
    Copy the code
    • Workarounds. First, generate an empty object usingcallMethods the bindingGeneratorInternal of a functionthis. After the constructor call, the empty object isGeneratorFunction instance object.
    function* F() {
      this.a = 1;
      yield this.b = 2;
      yield this.c = 3;
    }
    var obj = {};
    var f = F.call(obj);
    
    f.next();  // Object {value: 2, done: false}
    f.next();  // Object {value: 3, done: false}
    f.next();  // Object {value: undefined, done: true}
    
    obj.a // 1
    obj.b // 2
    obj.c // 3
    Copy the code
    • The traverser object is executed abovef, but the generated object instance isobjThat will beobjSwitch toF.prototype. Change F to a constructor, and you can execute itnewThe command.
    function* gen() {
      this.a = 1;
      yield this.b = 2;
      yield this.c = 3;
    }
    function F() {
      return gen.call(gen.prototype);
    }
    
    var f = new F();
    f.next();  // Object {value: 2, done: false}
    f.next();  // Object {value: 3, done: false}
    f.next();  // Object {value: undefined, done: true}
    
    f.a // 1
    f.b // 2
    f.c // 3
    Copy the code

application

  1. Retrieves all members of a nested array
    function* iterTree(tree) {
      if (Array.isArray(tree)) {
        for(let i=0; i < tree.length; i++) {
          yield* iterTree(tree[i]);
        }
      } else {
        yield tree;
      }
    }
    
    const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
    
    for(let x of iterTree(tree)) {
      console.log(x);
    }
    // a
    // b
    // c
    // d
    // e
    Copy the code
  2. Walk through a complete binary tree.
    Function Tree(left, label, right) {this.left = left; this.label = label; this.right = right; } // Here is the inorder traversal function. // Use a generator function because it returns an traverser. Function * inorder(t) {if (t) {yield* inorder(t. eft); if (t) {yield* inorder(t. eft); yield t.label; yield* inorder(t.right); Function make(array) {if (array.length == 1) return new Tree(null, array[0], null); return new Tree(make(array[0]), array[1], make(array[2])); } let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]); Var result = []; for (let node of inorder(tree)) { result.push(node); } result // ['a', 'b', 'c', 'd', 'e', 'f', 'g']Copy the code
  3. AjaxAsynchronous operation of
    • throughGeneratorFunction deploymentAjaxOperations can be expressed synchronously.Pay attention to,makeAjaxCallIn the functionnextMethod, you have to addresponseParameter, becauseyieldThe expression, which itself has no value, is always equal toundefined.
    function* main() {
      var result = yield request("http://some.url");
      var resp = JSON.parse(result);
        console.log(resp.value);
    }
    
    function request(url) {
      makeAjaxCall(url, function(response){
        it.next(response);
      });
    }
    
    var it = main();
    it.next();
    Copy the code
  4. Read the text file line by line.
    function* numbers() {
      let file = new FileReader("numbers.txt");
      try {
        while(!file.eof) {
          yield parseInt(file.readLine(), 10);
        }
      } finally {
        file.close();
      }
    }
    Copy the code
  5. Control flow management
    • If you have a multistep operation that is time-consuming, using a callback function, it might be written as follows.
    step1(function (value1) {
      step2(value1, function(value2) {
        step3(value2, function(value3) {
          step4(value3, function(value4) {
            // Do something with value4
          });
        });
      });
    });
    Copy the code
    • usingfor... ofLoops are automatically executed in sequenceyieldThe command feature provides a more general approach to control flow management.
    let steps = [step1Func, step2Func, step3Func]; function* iterateSteps(steps){ for (var i=0; i< steps.length; i++){ var step = steps[i]; yield step(); }}Copy the code
  6. The deployment ofIteratorinterface
    • usingGeneratorFunction that can be deployed on any objectIteratorInterface.
    function* iterEntries(obj) {
      let keys = Object.keys(obj);
      for (let i=0; i < keys.length; i++) {
        let key = keys[i];
        yield [key, obj[key]];
      }
    }
    
    let myObj = { foo: 3, bar: 7 };
    
    for (let [key, value] of iterEntries(myObj)) {
      console.log(key, value);
    }
    
    // foo 3
    // bar 7
    Copy the code
  7. As a data structure
    • GeneratorYou can think of it as a data structure, or more specifically, an array structure.
    function* doStuff() { yield fs.readFile.bind(null, 'hello.txt'); yield fs.readFile.bind(null, 'world.txt'); yield fs.readFile.bind(null, 'and-such.txt'); } for (task of doStuff()) {// Task is a function that can be used like a callback function}Copy the code
  8. Asynchronous application
    • The asynchronous application of es6-generator functions