JavaScriptの非匿名関数の美しさ

画像


いくつかの世論調査によると、 JavaScriptの匿名矢印関数はES-2015の最も人気のある機能であり、インターネット上の総合的なチュートリアルでも強調されています。 それらは紛れもなく非常に便利ですが、この短い記事では、 名前の付いた関数で少なくとも素晴らしい式-NFEを使用した例に注目します。


短いヘルプ


名前付き関数式 -JavaScriptの関数式の拡張機能で、式の一部として作成された関数に名前を付けることができます( FunctionExpression ):


let fe = function named(...) { /* function body */ }; 

全体のポイントは、 fe変数によって参照される関数内では、という名前で関数自体にアクセスできるということです。 関数内からのみ名前を書き換えることはできません!


使い方


たとえば、ある関数の呼び出し回数をカウントする装飾関数を作成する必要があります。 これは、NFEを使用して非常にエレガントに行うことができます。


 const count = f => function df() { df.calls = (df.calls || 0) + 1; return f(...arguments); }; 

ここでは、返されたdf関数にアクセスできるため、呼び出されたときに、 callsプロパティにカウンターを保存します。これは、必要に応じて読み取ることができます。


 const csum = count((x, y) => x + y); csum(5, 10); // 15 csum(50, 1); // 51 csum.calls; // 2 

または、関数呼び出しのすべての結果を保存します。


 const accum = f => function df() { let res = f(...arguments); (df.results = (df.results || [])).push(res); return res; }; 

いずれの場合でも、JavaScriptの関数はオブジェクトであるという事実を利用し、ローカルタスクで必要なプロパティを追加することができます。


明らかに、関数を呼び出すことができます。 NFEを再帰に使用すると特に便利です。 フィボナッチ数列のn番目のメンバーを検索するとします(何らかの理由で、誰もが遅かれ早かれこれを望みます)。


 const rf = function getfn(n) { return n > 2 ? getfn(n - 2) + getfn(n - 1) : 1; }; rf(1); // 1 rf(10); // 55 

ここで「テーリング」に注意を払う価値はありません。 しかし、 FunctionDeclarationを介して同じことを行う機能について -はい。 ただし、式関数には1つの利点があります。rfから別の変数に関数を転送する場合、内部で再帰呼び出しを編集する必要はありません。


タイマー実装のきちんとした例:


 const ping = (callback, t) => setTimeout(function pf() { callback(); setTimeout(pf, t); }, t); 

ここでは、不必要な変数を宣言する代わりに、NFEを引数としてリテラルとして使用します。 なぜsetIntervalではないのですか? コールバックの代わりに、タイマーの次のティックの前にプロミスの解決を待つ場合があります。


興味深い例は、NFEとIIFE( 即時起動関数式 )の組み合わせです。再帰関数の結果のみが必要な場合に使用します。 一度だけ:


 let data = { f10: function fact(n) { return n > 1 ? n * fact(n - 1) : 1; }(10) }; data.f10; // 3628800 

そうですか? さて、ここに本当の問題があります。自然数の集合で作用する厳密に増加する関数fがあります。 関数の値が与えられたyを超えない極値点xを見つけます。 このような関数の例は、 f(x) = 3 * x + 5またはf(x) = 2^x + 11です。


 const find = (f, y) => function bs(a, b) { if (a + 1 === b) return a; let m = Math.ceil((a + b) / 2); return f(m) <= y ? bs(m, b) : bs(a, m); }(-1, y + 1); find(x => 3 * x + 5, 200); // 65 find(x => Math.pow(2, x) + 11, 1000); // 9 

数学的な詳細には触れません。 実装を検討してください。


  1. 必要な検索関数には2つのパラメーターf、yがあり、IIFEの結果を返します。
  2. すぐに呼び出される関数は、ソリューションのバイナリ検索を実装し、最初の呼び出しは初期範囲を取得します。
  3. 検索自体は、後続の呼び出しにNFE名を使用する再帰によって実装されます。 検索関数を宣言せず、新しい変数を作成しません。

もちろん、前提条件のある単純なサイクルで同様の問題を解決できますが、これはコンソールコーディングには不可欠です。


最後に、スタックトレースについて少し説明します。 本体に例外がスローされたNFE関数があります。


 const fe = function named() { throw new Error('Something went wrong'); }; 

式に名前付き関数があるため、スタックトレースに表示されます。


 Uncaught Error: Something went wrong at named (<anonymous>:2:11) 

ただし、ES-2015以降では、多くの匿名関数式が実際に名前を持つ関数を作成し、コンテキストから除外します。


 const unnamed = function() { throw new Error('Something went wrong'); }; 

式の右側の関数は、左側の変数に関連付けられています。


 Uncaught Error: Something went wrong at unnamed (<anonymous>:2:7) 

しかし、これは常に可能とは限りません。 典型的な例は、オプションとしてIIFEを介した外部ライブラリスクリプトの初期化です。


 /** * @license * @description */ ;(function() { // some awesome code }.call(this)); 

または、別の関数を返す関数のよくある例:


 const bind = (f, ctx) => function() { return f.apply(ctx, arguments); }; 

出力用の変数はないため、例外の場合はanonymousが表示さます。 NFEは訴訟で少し役立つかもしれません。


短い結論


NFEは、さまざまなタスクをすばやくプロトタイプ化するために、簡潔でサポートされていないコードを書くのが得意です。 最終的なコードでそれらの美しさを使用するには、何度か考える必要があります。JavaScriptはすでに興味深い仕様でオーバーロードされています。



Source: https://habr.com/ru/post/J427461/


All Articles