JavaScript ES6: Test for arrow function, built-in function, regular function?

后端 未结 10 654
遇见更好的自我
遇见更好的自我 2020-11-27 06:16

Is there an elegant way to tell Harmony\'s slim arrow functions apart from regular functions and built-in functions?

The Harmony wiki states that:

相关标签:
10条回答
  • 2020-11-27 06:21

    Believe it or not...

    Testing for presence of "=>" in string representation of a function is likely the most reliable way (but not 100%).

    Obviously we can't test against either of 2 conditions you mentioned — lack of prototype property and lack of [[Construct]] as that might give false positives with either host objects or built-in ones that lack [[Construct]] (Math.floor, JSON.parse, etc.)

    We could, however, use good old Function.prototype.toString to check if function representation contains "=>".

    Now, I've always recommended against using Function.prototype.toString (so-called function decompilation) due to its implementation-dependent and historically unreliable nature (more details in State of function decompilation in Javascript).

    But ES6 actually tries to enforce rules on the way (at least) built-in and "user-created" (for the lack of better term) functions are represented.

    1. If Type(func) is Object and is either a Built-in function object or has an [[ECMAScriptCode]] internal slot, then

      a. Return an implementation-dependent String source code representation of func. The representation must conform to the rules below.

    ...

    toString Representation Requirements:

    • The string representation must have the syntax of a FunctionDeclaration FunctionExpression, GeneratorDeclaration, GeneratorExpession, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod depending upon the actual characteristics of the object.

    • The use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.

    • If the object was defined using ECMAScript code and the returned string representation is not in the form of a MethodDefinition or GeneratorMethod then the representation must be such that if the string is evaluated, using eval in a lexical context that is equivalent to the lexical context used to create the original object, it will result in a new functionally equivalent object. In that case the returned source code must not mention freely any variables that were not mentioned freely by the original function’s source code, even if these “extra” names were originally in scope.

    • If the implementation cannot produce a source code string that meets these criteria then it must return a string for which eval will throw a SyntaxError exception.

    I highlighted relevant chunks.

    Arrow functions have internal [[ECMAScriptCode]] (which you can track from 14.2.17 — evaluation of arrow function - to FunctionCreate to FunctionInitialize).

    This means they must conform to ArrowFunction syntax:

    ArrowFunction[In, Yield] :
      ArrowParameters[?Yield] [no LineTerminator here] => ConciseBody[?In]
    

    ..which means they must have => in Function.prototype.toString's output.

    You'll obviously need to ensure "=>" follows ArrowParameters and is not just something present in FunctionBody:

    function f() { return "=>" }
    

    As for reliability — remember that this behavior is/might not be supported by any/all engines at the moment and that host objects' representation might lie (despite specs efforts) for whatever reasons.

    0 讨论(0)
  • 2020-11-27 06:21

    Based documentation on mozilla.org and taking into account side effects of Use of the new operator and that page we could try to do something like:

    function isArrow (fn) {
      if (typeof fn !== 'function') return false
      try {
        new fn()
      } catch(err) {
       if(err.name === 'TypeError' && err.message === 'fn is not a constructor') {
        return true
       }
      }
      return false
    }
    
    console.log(isArrow(()=>{})) // true
    console.log(isArrow(function () {})) // false
    console.log(isArrow({})) // false
    console.log(isArrow(1)) // false
    
    
    let hacky = function () { throw new TypeError('fn is not a constructor') }
    console.log(isArrow(hacky)) // unfortunately true

    0 讨论(0)
  • 2020-11-27 06:22
    • Convert Function to string toString
    • removing all spaces of this string.
    • if ")=>" exists with index greater or equal to 1 => 99.99% is an arrow function .

          F.toString().replace(/\s+/g, '').indexOf(')=>')>=1
      

    DEMO :

    var fn1=function(e){
       e.target.value=new Date();
    };
    
    var fn2=(a,b)=> a+b;
    
    function isArrow(name,F){
             if(F.toString().replace(/\s+/g, '').indexOf(')=>')>=1){
                     console.log(`${name} is arrow-function`);
             }else{
               console.log(`${name} is classic-function`);
              }
    }
    
    isArrow('fn1',fn1);
    isArrow('fn2',fn2);

    0 讨论(0)
  • 2020-11-27 06:27

    I wrote this for Node, should work in Chrome as well.

    "Boundness" is detected (apparently, only on ES6) and reported as native && bound. This might or might not be an issue, depending on what you are using that information for.

    const flags = {
      function: f instanceof Function,
      name: undefined,
      native: false,
      bound: false,
      plain: false,
      arrow: false
    };
    
    if (flags.function) {
      flags.name = f.name || '(anonymous)';
      flags.native = f.toString().trim().endsWith('() { [native code] }');
      flags.bound = flags.native && flags.name.startsWith('bound ');
      flags.plain = !flags.native && f.hasOwnProperty('prototype');
      flags.arrow = !(flags.native || flags.plain);
    }
    
    return flags;
    
    0 讨论(0)
  • 2020-11-27 06:27

    As far as I can tell this should work:

    All non-arrow functions when converted to String START with 'function '. Arrow functions don't.

    Trying to test for the presence of '=>' is not a reliable way to test whether the function is an arrow or not because any non-arrow function can contain arrow-functions inside them and thus the '=>' can be present in their source-code.

    0 讨论(0)
  • 2020-11-27 06:29

    Arrow function of JavaScript does not have prototype property.

    function isArrowFunc(fn){
        return typeof(fn)=="function" && fn.prototype === undefined && !(/\{\s*\[native code\]\s*\}/).test(fn.toString());
    }
    
    isArrowFunc(()=>{}) // true
    isArrowFunc(function(){}) // false
    isArrowFunc(123) // false
    
    0 讨论(0)
提交回复
热议问题