This is the 9th day of my participation in Gwen Challenge

Is-generator-function is a dependency package of Koa, used to determine whether it is a generator function. The source code is 38 lines, and the logic is not complicated. The core code is the following:

module.exports = function isGeneratorFunction(fn) {
    if (typeoffn ! = ='function') {
        return false;
    }
    if (isFnRegex.test(fnToStr.call(fn))) {
        return true;
    }
    if(! hasToStringTag) {var str = toStr.call(fn);
        return str === '[object GeneratorFunction]';
    }
    if(! getProto) {return false;
    }
    if (typeof GeneratorFunction === 'undefined') {
        var generatorFunc = getGeneratorFunc();
        GeneratorFunction = generatorFunc ? getProto(generatorFunc) : false;
    }
    return getProto(fn) === GeneratorFunction;
};
Copy the code

First, use typeof to determine whether the target function is a function type. If it is not a function type, determine that it is not a Generator function.

IsFnRegex and fnToStr are defined as follows:

var fnToStr = Function.prototype.toString;
var isFnRegex = /^\s*(? :function)? A \ * /;
Copy the code

Function. The prototype. ToString is used to the entire Function block of code into a string, we do a simple test as follows:

let func1 = function* () {}
let func2 = function * () {}
let obj = {
    *fn(){}}console.log(fnToStr.call(func1)) // function* () {}
console.log(isFnRegex.test(fnToStr.call(func1))) // true
console.log(fnToStr.call(func2)) // function * () {}
console.log(isFnRegex.test(fnToStr.call(func2)))  // false
console.log(fnToStr.call(obj.fn)) // *fn() {}
console.log(isFnRegex.test(fnToStr.call(obj.fn))) // true
Copy the code

You can see that this regular expression matches function* and *func defined functions, but function* defined functions cannot be identified.

So if a regular match, through the Object. The prototype. The toString method of judgment. The code for hasToStringTag and toStr looks like this:

var toStr = Object.prototype.toString;
var hasToStringTag = typeof Symbol= = ='function' && typeof Symbol.toStringTag === 'symbol';
Copy the code

Because of increased ES6 Symbol. ToStringTag, through this property to modify the Object. The prototype. ToString return value, as shown below:

let user = {
    [Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
Copy the code

So only in the clear. There is no Symbol toStringTag conditions, with the Object. The prototype. ToString type judgment result is reliable.

module.exports = function isGeneratorFunction(fn) {
    / /...
    if(! hasToStringTag) {var str = toStr.call(fn);
        return str === '[object GeneratorFunction]';
    }
    / /...
}
Copy the code

If none of the above conditions are met, the last step is to check whether the prototype property of this function matches the Prototype property of GeneratorFunction.

GetPrototypeOf (object.getProtoTypeof); if not, return false. If object.getProtoTypeof exists, create an empty Generator function. Check whether the object function is a Generator function by checking whether the ptototype of the Genertator function is equal to the prototype of the object function.

var GeneratorFunction;  // Store prototype for Generator functions
var getProto = Object.getPrototypeOf;
var getGeneratorFunc = function () { // This function creates an empty Generator function
    if(! hasToStringTag) {return false;
    }
    try {
        return Function('return function*() {}') (); }catch (e) {
    }
};

module.exports = function isGeneratorFunction(fn) {
    / /...
    if (typeof GeneratorFunction === 'undefined') {
        // Create an empty Generator function
        var generatorFunc = getGeneratorFunc(); 
        // Get the prototype of the Generator function
        GeneratorFunction = generatorFunc ? getProto(generatorFunc) : false;  
    }
    // Check whether the target function is a Generator according to prototype
    return getProto(fn) === GeneratorFunction;  
}
Copy the code

conclusion

The judgment logic of the Generator function is as follows:

  1. Check whether it is a function type through typeof. If yes, go to Step 2.
  2. Convert it to a string and check whether it conforms to the Generator function definition through regular matching. If yes, return true. If no, go to Step 3.
  3. Check whether the current environment hasSymbol.toStringThere is no basisObject.prototype.toStringCheck whether it is a Generator functionSymbol.toStringThen proceed to Step 4.
  4. Determine whether the environment hasObject.getPrototypeOfIf no, return false. If yes, check whether the target function is a Generator function by checking whether the prototype of the current function matches that of the Generator function

contrast

Yesterday I wrote a co source code analysis article, in CO there is also a Generator function of the judgment logic, the implementation of the idea is not the same as the idea of this article, interested friends can compare and see.

// check whether obj is a Generator function
function isGeneratorFunction(obj) {
    var constructor = obj.constructor;
    if (!constructor) return false;
    if ('GeneratorFunction'= = =constructor.name| | 'GeneratorFunction'= = =constructor.displayName) 
        return true;
    return isGenerator(constructor.prototype);
}

// Check whether obj is a Generator object
function isGenerator(obj) {
    return 'function'= =typeof obj.next && 'function'= =typeof obj.throw;
}
Copy the code