VUE3.0 をすぐに使い始める方法: 学習
関連の推奨事項に入る: JavaScript 学習チュートリアル
関数型プログラミングに関する多くの説明を見てきましたが、そのほとんどは理論レベルであり、一部は純粋な関数型プログラミング言語のみを対象としています。ハスケルなど。この記事の目的は、私の目から見た JavaScript での関数型プログラミングの具体的な実践について話すことです。「私の目から見て」という理由は、私が述べていることは私の個人的な意見を表しているだけであり、一部の厳密な概念と矛盾する可能性があることを意味します。
この記事では、正式な概念の紹介の多くを省略し、JavaScript の関数コードとは何か、関数コードと一般的な記述の違いは何か、関数コードがもたらす利点、および共通の関数モデルとは何なのかを示すことに重点を置きます。
関数型プログラミングは、関数を主なキャリアとして使用するプログラミング手法として理解できると思います。関数を使用して一般式
を逆アセンブルし、抽象化することの利点は何ですか?主なポイントは、
より明確なセマンティクス、より高い再利用性、より高い保守性、より優れたスコープ制限、およびより少ない副作用です。次の例は、JavaScript コード
の特定の関数式です。
// 配列内の各単語を大文字にします。最初の文字 // 一般的な書き方 const arr = ['apple', 'pen', 'apple-pen']; for(const i in arr){ const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1); コンソール.ログ(arr); // 関数の書き方 - function upperFirst(word) { 戻り word[0].toUpperCase() + word.slice(1); 関数 wordToUpperCase(arr) { リターン arr.map(upperFirst); console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // 関数的な書き方2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1))) ;
状況がより複雑になると、式の書き方にはいくつかの問題が発生します:
意味がわかりにくい、保守が徐々に困難になる、再利用性が低い、より多くのコードが生成される、多くの中間変数が生成される。関数型プログラミングは、上記の問題を解決するのに適しています。まず、関数の書き方 1 を参照してください。関数のカプセル化を使用して関数を分解し (粒度は一意ではありません)、それらを異なる関数にカプセル化してから、呼び出しを組み合わせて目的を達成します。これにより、式が明確になり、保守、再利用、拡張が容易になります。次に、高階関数を使用して、Array.map は配列トラバーサルの for...of を置き換え、中間の変数と演算を減らします。
関数記述方法 1 と関数記述方法 2 の主な違いは、関数を後で再利用できるかどうかを考慮できることです。そうでない場合は、後者の方が優れています。
上記の関数記述方法 2 から、関数コードを記述する過程で水平拡張、つまり複数層の入れ子が発生しやすいことがわかります。以下に極端な例を示します。
Javascriptコード
// 数値の合計を計算 // 一般的な書き方 console.log(1 + 2 + 3 - 4) // 関数記述関数 sum(a, b) { a + b を返します。 関数 sub(a, b) { a - b を返します。 console.log(sub(sum(sum(1, 2), 3), 4); この例は、水平拡張の極端なケースのみを示しています。関数のネストされたレベルの数が増加し続けると、コードは読みにくくなります。パフォーマンスが大幅に低下し、間違いが発生しやすくなります。 この場合、次のようなチェーン最適化など、複数の最適化手法が考えられます。 // 書き込みを最適化します (お読みのとおり、これは lodash のチェーン書き込みです) JavaScript コード const utils = { チェーン(a) { this._temp = a; これを返します。 }、 合計(b) { this._temp += b; これを返します。 }、 サブ(b) { this._temp -= b; これを返します。 }、 価値() { const _temp = this._temp; this._temp = 未定義; _temp を返します; } }; console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
このように書き換えると、全体の構造が明確になり、チェーンの各リンクが明確になります。何をすればよいのかも簡単に示すことができます。関数のネストとチェーンを比較するもう 1 つの良い例は、コールバック関数と Promise パターンです。
Javascript コード
// 2 つのインターフェースを連続してリクエストします // コールバック関数 import $ from 'jquery', (rs) => { if(rs){ $.post('a/url/to/another/target', (rs2) => { if(rs2){ $.post('a/url/to/third/target'); } }); } }); // 'catta' からのインポート リクエストを約束します。 // catta は fetch、jsonp、ajax をサポートし、依存関係を持たない軽量のリクエスト ツールです request('a/url/to/target') .then(rs => rs ? $.post('a/url/to/another/target') : Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());
コールバック関数のネストレベルと単一レベルの複雑さが増加すると、肥大化して保守が困難ですが、Promise のチェーン構造は、複雑さが高く、階層的な分離が非常に明確な場合には垂直に拡張することができます。
一般的な関数型プログラミング モデルの
は、解放されないコード ブロック内にローカル変数を保持できます。
クロージャの概念は、誰でも多かれ少なかれ知っていて、この機能を使用していると思います
。バッグは私たちを連れて行ってくれますか?
まずクロージャの作成方法を見てみましょう。
Javascript コード
// クロージャ関数を作成します makeCounter() { k = 0 とします。 戻り関数() { ++k を返します。 }; const counter = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter この関数のコード ブロックは、返された関数でローカル変数 k を参照します。関数が実行されると、システムによってリサイクルされ、クロージャが生成されます。このクロージャの機能は、ローカル変数を「保持」して、内部関数の呼び出し時に変数を再利用できるようにすることです。グローバル変数とは異なり、この変数は関数内でのみ参照できます。
言い換えれば、クロージャは実際には、関数にとってプライベートないくつかの「永続変数」を作成します。
この例から、クロージャを作成するための条件は次のとおりであると結論付けることができます
。 内部関数と外部関数が存在する。 クロージャの主な目的は、外部関数を定義することです。一部の関数。ドメイン限定の永続変数。これらの変数はキャッシュや中間計算などに使用できます。
Javascript コード
// 単純なキャッシュ ツール // 匿名関数はクロージャを作成します const queue = (function() { const ストア = {}; 戻る { get(キー) { ストア[キー]を返します; }、 set(キー、値) { ストア[キー] = val; } } }()); cache.set('a', 1); queue.get('a'); // 1
上記の例は、ストア オブジェクトを常に参照できるようにするための単純なキャッシュ ツールの実装です。 、リサイクルされません。
クロージャの欠点: 永続変数は正常に解放されず、メモリ領域を占有し続けるため、メモリの無駄が発生しやすいため、通常は追加の手動クリーンアップ メカニズムが必要です。
を受け入れるか返す関数は、
非常に寒い言葉のように聞こえますが、実際にはそれを使用しますが、それらの名前はわかりません。 JavaScript言語は、JavaScript関数が一流の市民であり、パラメーターと別の関数の返品値として使用できます。
多くの場合の
ネイティブの高次関数を見ることができ
ます
つまり、セット内の各項目は同じ変換を受けて、新しいセット
マップを生成します。高階関数として、マッピングの論理
Javascript コード
として関数パラメーターを受け取ります。 // の各項目に 1 を追加します。作成する配列 新しい配列 // 一般的な書き方 const arr = [1,2,3] const rs = []; rs.push(++n); コンソール.ログ(rs) // マップを書き換える const arr = [1,2,3]; const rs = arr.map(n => ++n);
for...of ループを使用して配列を走査すると、元の配列を変更するリスクがありますが、
map 関数は必要な操作をカプセル化しているため、マッピング ロジックの関数実装のみを気にする必要があり、コードの量とサイドのリスクが軽減されます。効果。
関数のいくつかのパラメータを与え、他のパラメータを受け入れる新しい関数を生成します。
この用語はあまり聞かないかもしれませんが、undescore または lodash を使用したことがある人なら誰でも見たことがあります。
JavaScript コードを
カリー化した魔法の _.partial 関数があります。
// ターゲット ファイルのベース パスへの相対パスを取得します。 // 一般的な記述方法は const BASE = '/path/to/base'; です。相対パス(BASE, '/some/path'); // _.parical 書き換え const BASE = '/path/to/base' constrelativeFromBase = _.partial(path.relative, BASE); constrelativePath =relativeFromBase('/some/path');
_.partial を通じて、新しい関数relativeFromBase を取得します。この関数が呼び出されるとき、それは path.relative を呼び出すことと同じであり、最初のパラメータはデフォルトで BASE に渡されます。渡された後続のパラメータは順番に追加されます。
この場合、本当に達成したいのは、パスを基準とするのではなく、毎回 BASE を基準とする相対パスを取得することです。カリー化により、関数の一部のパラメーターのみを考慮できるようになり、関数の目的がより明確になり、関数の呼び出しがより簡単になります。
複数の関数の機能を組み合わせて新しい関数を作成することです。lodash
という合成メソッド (現在はフローと呼ばれています) のJavascript コードを
初めて見たかもしれません。
// 配列内の各単語を大文字にし、Base64 を実行します。 //一般的な書き方(その一つ) const arr = ['pen', 'apple', 'applypen']; const rs = []; rs.push(btoa(w.toUpperCase())); } console.log(rs); // _.flow 書き換え const arr = ['pen', 'apple', 'applypen']; const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow は、大文字変換関数と Base64 変換関数の機能を結合して、新しい関数を生成します。パラメータ関数として使用したり、後で再利用したりすると便利です。
私自身の観点から見ると、JavaScript 関数プログラミングに対する私の理解は、多くの従来の概念とは異なる可能性があります。高階関数だけが関数型プログラミングにカウントされるわけではないと思います。その他、通常の関数の結合呼び出しやチェーン構造など、関数をメインとして使用するものであれば、関数型プログラミングの範疇に属すると思います。運送業者。
そして、関数型プログラミングは必要ではないし、必須のルールや要件であるべきでもないと思います。オブジェクト指向や他の考え方と同様、これも一つの方法です。ほとんどの場合、概念に限定されるのではなく、いくつかの組み合わせを使用する必要があります。
関連する推奨事項: JavaScript チュートリアル
上記は JavaScript 関数プログラミングの詳細な説明です。詳細については、PHP 中国語 Web サイトの他の関連記事に注目してください。