JavaScript ES6:测试箭头函数、内置函数、常规函数? [英] JavaScript ES6: Test for arrow function, built-in function, regular function?

查看:25
本文介绍了JavaScript ES6:测试箭头函数、内置函数、常规函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有一种优雅的方式将 Harmony 的细长箭头函数与常规函数内置函数区分开来?

Harmony wiki 指出:

<块引用>

箭头函数类似于内置函数,因为缺少 .prototype 和任何 [[Construct]] 内部方法.所以 new (() => {}) 会抛出一个 TypeError 否则箭头就像函数

这意味着,您可以测试箭头函数,例如:

!(()=>{}).hasOwnProperty("prototype")//true!(function(){}).hasOwnProperty("prototype")//false

但测试也会为任何内置函数返回 true,例如setTimeoutMath.min.

如果您获取源代码并检查它是否是 本机代码",它在 Firefox 中可以正常工作,但它似乎不太可靠或可移植(其他浏览器实现,NodeJS/iojs):

setTimeout.toSource().indexOf("[本地代码]") >-1

小型 GitHub 项目 node-is-arrow-function 依赖于针对函数源代码的 RegExp 检查,这不是那么整洁.

我尝试了 JavaScript 解析器 acorn,看起来工作得很好 - 即使这有点矫枉过正.

acorn = require("./acorn");函数 fn_sample(a,b){c = (d,e) =>d-e;f = c(--a, b) * (b, a);返回 f;}功能测试(fn){fn = fn ||fn_sample;尝试 {acorn.parse("(" + fn.toString() + ")", {ecma版本:6,onToken:函数(令牌){if(typeof token.type == "object" && token.type.type == "=>"){console.log("ArrowFunction found", token);}}});}赶上(e){console.log("错误,可能是[本机代码]造成的");console.log(e.message);}}出口.测试=测试;

解决方案

信不信由你...

测试函数的字符串表示中是否存在=>"可能是最可靠的方法(但不是 100%).

显然,我们无法针对您提到的两个条件中的任何一个进行测试 - 缺少原型属性和缺少 [[Construct]] 因为这可能会给宿主对象或内置对象带来误报缺少[[Construct]](Math.floorJSON.parse等)

然而,我们可以使用旧的 Function.prototype.toString 来检查函数表示是否包含=>".

现在,我一直推荐反对使用Function.prototype.toString(所谓的函数反编译),因为它的实现——依赖和历史上不可靠的性质(Javascript 中的函数反编译状态).

但 ES6 实际上尝试执行规则 在途中(至少)表示了内置和用户创建"(因为没有更好的术语)功能.

<块引用>

  1. 如果 Type(func) 是 Object 并且是内置函数对象或有一个 [[ECMAScriptCode]] 内部槽,然后

    一个.返回 func 的依赖于实现的 String 源代码表示.表示必须符合以下规则.

...

toString 表示要求:

  • 字符串表示必须具有 FunctionDeclaration FunctionExpression、GeneratorDeclaration、GeneratorExpession、ClassDeclaration、ClassExpression、ArrowFunction、MethodDefinition 或 GeneratorMethod 取决于实际对象的特征.

  • 表示字符串中空格、行终止符和分号的使用和放置是依赖于实现.

  • 如果对象是使用 ECMAScript 代码定义的,并且返回的字符串表示形式不是 MethodDefinition 或GeneratorMethod 那么表示必须是这样的,如果字符串被评估,在词法上下文中使用 eval相当于用于创建原始对象的词法上下文,它将产生一个新的功能等效的对象.在这种情况下返回的源代码不得随意提及任何变量原始函数的源代码没有随意提及,即使如果这些额外"名称最初在范围内.

  • 如果实现不能产生满足这些条件的源代码字符串,那么它必须返回一个 eval 将抛出的字符串SyntaxError 异常.

我突出显示了相关的块.

箭头函数具有内部 [[ECMAScriptCode]](您可以从 14.2.17 跟踪 — 箭头函数的评估 - 到 FunctionCreateFunctionInitialize).

这意味着它们必须符合 ArrowFunction 语法:

ArrowFunction[In, Yield] :ArrowParameters[?Yield] [此处没有 LineTerminator] =>ConciseBody[?In]

..这意味着它们必须在 Function.prototype.toString 的输出中有 =>.

您显然需要确保=>"跟在 ArrowParameters 之后,而不只是出现在 FunctionBody 中:

function f() { return "=>"}

至于可靠性——请记住,目前任何/所有引擎都/可能不支持这种行为,并且无论出于何种原因,宿主对象的表示都可能撒谎(尽管规范努力).

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:

Arrow functions are like built-in functions in that both lack .prototype and any [[Construct]] internal method. So new (() => {}) throws a TypeError but otherwise arrows are like functions

Which means, you can test for arrow functions like:

!(()=>{}).hasOwnProperty("prototype") // true
!(function(){}).hasOwnProperty("prototype") // false

But the test will also return true for any built-in function, e.g. setTimeout or Math.min.

It sort of works in Firefox if you get the source code and check if it's "native code", but it doesn't seem much reliable nor portable (other browser implementations, NodeJS / iojs):

setTimeout.toSource().indexOf("[native code]") > -1

The small GitHub project node-is-arrow-function relies on RegExp-checks against the function source code, which isn't that neat.

edit: I gave the JavaScript parser acorn a try and it seems to work quite okay - even though it's pretty overkill.

acorn = require("./acorn");

function fn_sample(a,b){
    c = (d,e) => d-e;
    f = c(--a, b) * (b, a);
    return f;
}

function test(fn){
    fn = fn || fn_sample;
    try {
        acorn.parse("(" + fn.toString() + ")", {
            ecmaVersion: 6,
            onToken: function(token){
                if(typeof token.type == "object" && token.type.type == "=>"){
                    console.log("ArrowFunction found", token);
                }
            }
        });
    } catch(e) {
        console.log("Error, possibly caused by [native code]");
        console.log(e.message);
    }
}

exports.test = test;

解决方案

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.

这篇关于JavaScript ES6:测试箭头函数、内置函数、常规函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆