27

通常の関数や組み込み関数とは別に、Harmony のスリム アロー関数を区別するエレガントな方法はありますか?

Harmony wikiには、次のように記載されています。

アロー関数は、.prototypeと [[Construct]] 内部メソッドがないという点で組み込み関数に似ています。したがって、 new (() => {}) は TypeError をスローしますが、それ以外の場合、矢印は関数のようです

つまり、次のような矢印関数をテストできます。

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

ただし、テストは、またはなどtrueの組み込み関数に対しても返されます。setTimeoutMath.min

ソースコードを取得して"native code"それが.

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

小さな GitHub プロジェクトnode-is-arrow-functionは、関数のソース コードに対する RegExp チェックに依存していますが、これはそれほどきちんとしたものではありません。

編集: JavaScript パーサーacornを試してみましたが、かなりやり過ぎですが、問題なく動作するようです。

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

9 に答える 9

13

信じようと信じまいと...

関数の文字列表現に "=>" が存在するかどうかをテストするのが、おそらく最も信頼できる方法です (ただし 100% ではありません)。

明らかに、あなたが言及した2つの条件のいずれかに対してテストすることはできませ[[Construct]]ん。[[Construct]]Math.floorJSON.parse

ただし、古き良きを使用Function.prototype.toStringして、関数表現に「=>」が含まれているかどうかを確認できます。

現在、実装に依存し、歴史的に信頼できない性質があるため、(いわゆる関数の逆コンパイル)を使用しないことを常に推奨てきました(詳細についてはJavascript における関数の逆コンパイルの状態を参照してください)。Function.prototype.toString

しかし、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 — アロー関数の評価 - FunctionCreateからFunctionInitializeまで追跡できます)。

これは、ArrowFunction 構文に準拠する必要があることを意味します。

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

..つまり、 => inFunction.prototype.toStringの出力が必要です。

明らかに、「=>」が ArrowParameters に続き、FunctionBody に存在するだけのものではないことを確認する必要があります。

function f() { return "=>" }

信頼性については、現時点ではこの動作が一部またはすべてのエンジンでサポートされている/サポートされていない可能性があること、およびホストオブジェクトの表現が何らかの理由で (仕様の努力にもかかわらず) 嘘をつく可能性があることを覚えておいてください。

于 2015-01-30T10:55:45.623 に答える
0

Ron S のソリューションはうまく機能しますが、誤検知を検出できます。

/** Check if function is Arrow Function */
const isArrowFn = (fn) => (typeof fn === 'function') && /^[^{]+?=>/.test(fn.toString());

/* False positive */
const fn = function (callback = () => null) { return 'foo' }

console.log(
  isArrowFn(fn)  // true
)

于 2019-09-07T11:58:39.060 に答える