JavaScriptの楽しいインタビュー:関数型プログラミングとは何ですか?

JavaScriptの分野での関数型プログラミングは本当にホットな話題となっています。ほんの数年前には、JavaScriptプログラマの多くは、さらに関数型プログラミングであるかわからないが、過去3年間で、私は大規模な関数型プログラミングのアイデアに含まれている各アプリケーションの大規模なコードベースを見てきました使用しています。

関数型プログラミングの組み合わせによって(FP略す)純関数避けるために、ソフトウェア・プロセスを構築する状態共有変数データ、および副作用の発生を。関数型プログラミングは、宣言型プログラミング命令型プログラミングではなく、すべてのアプリケーション・フローの状態が純粋な関数です。オブジェクト指向プログラミングとは対照的に、アプリケーションの状態とは、通常、オブジェクトのメソッドを共有していると考えられます。

関数型プログラミングはプログラミングパラダイムそれが定義されたソフトウェアアーキテクチャの原則の考え方のいくつかの基本的な方法に基づいていることを意味し、さらにプロセス指向プログラミングとオブジェクト指向プログラミングを含む他のプログラミングパラダイムの例。

比較命令型プログラミングやオブジェクト指向プログラミングコードは、よりコンパクトで、より容易に予測可能なテスト機能する傾向があります。あなたがこの方法を熟知や単語のいくつかの一般的なパターンに関連していない場合でも、関数型プログラミングコードも初心者のための非常にコンパクトと関連文書を見ることができます理解することがより困難になることがあります。

あなたは関数型プログラミングに関連する用語の検索を開始した場合は、すぐに壁にぶつかることがあり、専門用語の多くは初心者を威嚇することができます。単に少しも軽く彼らの学習曲線を議論していますが、いくつかの時間のためのJavaScriptプログラミングに従事してきた場合は、プロジェクト内の関数型プログラミングのアイデアやツールの多くを使用している必要があります。

あなたを怖がらせるために新しい単語を聞かせてはいけません。彼らはそれが聞こえるより容易になります。

最も難しい部分は、なじみのない言葉の束があなたの頭をいっぱいにできるようにするということができます。彼らはあなたを習得する前にも、以下のこれらの用語の意味を理解する必要があるため、様々な用語は、無実を見て:

  • ピュア機能
  • 関数合成
  • 共有状態を避けます
  • ステータス変更を避けます
  • 副作用を避けます

純粋な関数は次のように定義されています。

  • それぞれが同じ入力を与えられ、その出力は常に同じです
  • 副作用なし

多くの機能の純粋な機能は、(式は、プログラムの動作のそれに対応する値を変更せずに交換することができるならば、式は参照透明性と呼ばれている)、透明性への参照を含む、関数型プログラミングに重要です。

白は、関数が任意の外部の状態を使用していないことを意味し、同じ出力を得る常に同じ入力であることを引用した透明度:

function plusOne(x) {
    return x + 1; } 复制代码

上記の例では、私たちが交換するために6を使用することができ、透明性関数への参照であるplusOne(5)関数呼び出しを。詳しい説明は、スタックオーバーフローを参照することができます-参照透明性とは何ですか?

組み合わせ関数は、二つ以上の機能が新しい機能や演算処理を行うことを産生するために組み合わされることを意味します。例えば、結合機能は、f.g.センス組成物を指す...)JavaScriptでのと等価ですf(g(x))ライティングソフトの使用は非常に重要なステップであることを理解するために、関数型プログラミングの組み合わせの機能を理解します。

状態シェア

これは、共有メモリ空間の範囲内の共有変数またはオブジェクトのいずれかの状態が存在する、又はスコープとの間に渡されたオブジェクトの属性として使用されることを意味します。スコープは、共有またはグローバルスコープ閉鎖範囲を含むことができます。オブジェクト指向プログラミングでは、オブジェクトは、スコープを共有し、他のオブジェクトの間であると属性を追加することによって、典型的です。

問題は、機能の役割を理解するために、状態のシェアは、あなたが過去、関数内で使用される各共有変数や影響を理解しなければならないということです。

あなたはユーザーオブジェクトを保存する必要があると、あなたのsaveUser()機能は、サーバー上のインターフェイスへの要求を開始します。同時に、ユーザーが操作の頭部、コール置き換えるために持っていたupdateAvatar()別のトリガーとなると同時に、ヘッドを交換するsaveUser()要求を。あなたが保存すると、サーバーは、他のAPI呼び出しが変更したり、サーバーの応答上で同期させるメモリの内容のいずれかを交換する必要があり、標準のユーザオブジェクトを返します。

しかし、ここで、第二の応答時間は、最初よりも早く戻ります。ときに最初の応答(期限切れ)を返すので、新しいヘッドが戻って古いヘッドに交換し、メモリから消去されます。これは、条件との競合の一例である - 状態と共通のバグを共有についてです。

もう一つの状態は頻繁に通話機能に関するよくある質問と共有ための機能やタイミング関連の状態シェアの役割ので、カスケード故障の原因になりますを変更することです:

// 在状态共享的函数中,函数调用的顺序会导致函数调用的结果的变化
const x = {
  val: 2
};

const x1 = () => x.val += 1; const x2 = () => x.val *= 2; x1(); x2(); console.log(x.val); // 6 // 同样的例子,改变调用顺序 const y = { val: 2 }; const y1 = () => y.val += 1; const y2 = () => y.val *= 2; y2(); y1(); // 改变了结果 console.log(y.val); // 5 复制代码

私たちが共有状態を避けるためにしている場合は、関数呼び出しのタイミングと順序は、関数呼び出しの結果は変わりません。同じ入力を与えられた純粋な機能は、常に同じ出力を得ます。これは、関数呼び出しが完全に独立して、我々は簡素化し、復興基本的に変更することができます。ファンクションどこかに変更する、または関数呼び出しの順序は、プログラムの他の部分に影響を与えたり、損傷しません。

const x = {
  val: 2
};

const x1 = x => Object.assign({}, x, { val: x.val + 1}); const x2 = x => Object.assign({}, x, { val: x.val * 2}); console.log(x1(x2(x)).val); // 5 const y = { val: 2 }; // 由于不存在对外部变量的依赖 // 所以我们不需要不同的函数来操作不同的变量 // 此处故意留白 // 因为函数不变,所以我们可以以任意顺序调用这些函数任意次 // 而且还不改变其他函数调用的结果 x2(y); x1(y); console.log(x1(x2(y)).val); // 5 复制代码

上記の例では、使用Object.assign()方法を、次にコピーする最初のパラメータとして空のオブジェクトに渡されxなくその場で変更するよりも、属性xこの例では、使用しないObject.assign()ことを、それは新しいオブジェクトを作成するための簡単なスクラッチと同等ですが、これは代わりに、一般的なモデルを使用してのJavaScriptで変換の既存の状態のコピーを作成することです、我々は最初の例であることを実証しました。

あなたは慎重に、この例の場合は読みconsole.log()関数合成:文を、私はすでにいくつかのものを言及していることに注意してください。:言った知識の前に、組み合わせた機能は次のようになりますことを思い出してくださいf(g(x))この場合にはx1(x2())、それがありますx1.x2

もちろん、あなたが変更した場合の合成出力の順序も変更されます。実行順序は、やはり重要です。f(g(x))常にではないと同等g(f(x))、もはや大したことで機能、外部の物変数を心配する必要はありません。あなたが影響を与えるの機能または使用を知っている限り非純粋関数では、関数は、各変数の全体の歴史を行われてきたものを完全に認識することはできません。

タイミング依存の関数呼び出しを削除し、あなたは完全にバグのこのタイプを除外します。

不変性

不変オブジェクトは、オブジェクトが作成されると、もはや変更することができることを意味しません。つまり、オブジェクトが作成された後に変更可能なオブジェクトを変更することができ反するものです。不変性は、この機能せず、データフロープログラムが失われますので、状態履歴の損失は、その後、あなたのプログラムは、常に奇妙なバグを出てくる、関数型プログラミングのコア概念です。

JavaScriptでは、入れていないconstと不変性は混乱。const作成後にバウンドもはや変数名に割り当てられていないことができます。const不変オブジェクトを作成しないでください。使用しconst作成された変数は、もはや割り当てることができますが、オブジェクトのプロパティを変更することができます。

完全に不変オブジェクトを変更することはできませんされています。それが真の不変の値になるように、あなたは深いオブジェクトをフリーズすることができます。JavaScriptは、第一の層内のオブジェクトを凍結する方法があります。

const a = Object.freeze({
  foo: 'Hello',
  bar: 'world', baz: '!' }); a.foo = 'Goodbye';// Error: Cannot assign to read only property 'foo' of object Object 复制代码

これは、次のような、不変の浅いを凍結するための唯一の方法であります:

const a = Object.freeze({
  foo: { greeting: 'Hello' }, bar: 'world', baz: '!' }); a.foo.greeting = 'Goodbye'; console.log(`${ a.foo.greeting }, ${ a.bar }${a.baz}`);// Goodbye world! 复制代码

私たちは、元のプロパティには、凍結されたトップ層は不変であり、見ることができます。オブジェクトの属性値は、そのオブジェクトがまだ変数である場合は、(など、アレイを含みます)。あなたは全体のオブジェクトツリーをトラバースない限り、それが層をフリーズします。

多くの関数型プログラミング言語では、不変で、特殊なデータ構造は、このようなデータ構造が効果的に奥行きを凍結することができ、検索ツリーデータ構造と呼ばれています。

ツリー検索、共有構造の操作の特定の種類のパフォーマンスを行う、オブジェクトがコピーされた後で、共有メモリ空間を参照することによっては、このようにしてメモリを節約し、変化しないままで大幅に改善しています。

たとえば、オブジェクトツリーのルートにアイデンティティのコントロールの使用を比較することができます。同じのアイデンティティ場合は同じのアイデンティティあれば、あなたは違いを比較するためにツリー全体片を横断する必要はありません。

などのJavaScriptのライブラリツリーのいくつかの比較的良好な使用があるImmutable.jsとはMori

たぶん私が使用しているライブラリは、私は不変の状態の多くを必要とする大規模プロジェクトで使用することを好みますImmutable.js

副作用

関数呼び出しは、機能が加え以外の値を返します。副作用を指し、また、メイン呼び出し機能の追加効果を生み出します。だけでなく、リターンの副作用の機能値、だけでなく、他のことを実行します。

  • 外部のオブジェクトまたは変数の属性(またはグローバル変数親スコープチェーン変数関数)を変更します
  • コンソールに出力を印刷
  • 私は、画面に何かを書きました
  • 私は、ファイルに何かを書きました
  • 私はネットワークに何かを書きました
  • トリガ外部プロセス
  • 副作用を持っている他の関数を呼び出します

関数型プログラミングでの副作用は、ほとんどの時間は、プログラムの明確な役割となるように、避けるべきである、彼らはまた、テストされる可能性が高いです。

Haskell純粋に独立してパッケージ化機能から他のプログラミング言語のモナドは常に副作用を使用しています。内容についてはあまりモナドは、あなたが見つけるために行くことができます。

しかし、あなたは今、知っておく必要があり、副作用があなたのソフトウェアは、拡張、改造、デバッグ、テストが容易になるように、お使いのソフトウェアから独立して実施し、維持する必要があるということです。

これは、モジュールを切り離し、管理ステータスおよびレンダリング成分を分離するために、ユーザーを奨励するための最もフロントエンドのフレームワークです。

高階関数によって、再利用性を向上させます

関数型プログラミングは、ツールを使用してデータを処理するための機能の複雑な一連の傾向が見られました。オブジェクト指向プログラミング方法及び被験者のデータ傾向があり、これらの方法は、多くの場合、特定のコンポーネントまたはインスタンスに含ま良いデータ、に設計された動作をマージするために使用することができます。

関数型プログラミングでは、任意のタイプのデータは、同じ位置に、同じであるmap()が、パラメータとしての機能を受信するための機能は、オブジェクト、文字列、数値、またはデータの任意のタイプを横断することができ、このパラメータを適切に扱う機能することができますデータ型指定されました。このプログラミング機能は、高階関数の特性によって達成されます。

JavaScript関数は、ファーストクラスの市民は、私たちはデータとしての機能を扱うことができますビュー守るです - 変数、他の関数、関数に渡された関数に機能を割り当て、その関数の戻り値をできるようにします...

高階関数は、関数またはリターン機能、またはその両方の関数として任意のパラメータを受信することができる機能を指します。高次機能が頻繁に使用されています。

  • フロー制御非同期コールバック、約束,,モナドなどの抽象的または独立した操作...
  • さまざまなデータ型を処理できるユーティリティ関数を作成します。
  • 多重化または結合関数作成されたオブジェクトのカリー化関数で使用されるいくつかのパラメータまたは機能
  • その後、それらのいくつかの関数としてパラメータのセットを受信して​​は、組み合わせとして返します。

コンテナ、ファンクタ、リスト、流れ

ファンクタをマッピングすることができるものの一種です。言い換えれば、それはコンテナインターフェイスで、インターフェイスを使用することができますapply関数内で値に(翻訳フレーズあまりにも奇妙な、スキルは十分ではありません。それはオリジナルのインターフェースがあるコンテナの適用するために使用することができますその中の値への関数。) 。

以前の私たちは、同じ知っているmap()機能は、複数のデータ型に対して実行することができます。それは達成するためにファンクタのAPIの利用を高めるために、マッピング操作です。キー流量制御操作map()インタフェース機能を使用。これがあればArray.prototype.map()場合、コンテナが配列され、他のデータ構造は、限り、彼らが提供してファンクタもmap()APIを。

のは、見てみましょうArray.prototype.map()ように、マッピング機能から抽象データ型を許可する方法map()関数は任意のタイプのデータに使用することができます。私たちは、作成double()2の操作を掛け、着信パラメータをマッピングする機能を:

const double = n => n * 2;
const doubleMap = numbers => numbers.map(double); console.log(doubleMap([2, 3, 4])); // [ 4, 6, 8 ] 复制代码

私たちは、ゲーム操作にターゲットにする場合は、スコアはそれを倍増するために取得してみましょうか?あなただけがする必要があるdouble()関数を渡すmap()の値にほとんど変化:

const double = n => n.points * 2;

const doubleMap = numbers => numbers.map(double); console.log(doubleMap([ { name: 'ball', points: 2 }, { name: 'coin', points: 3 }, { name: 'candy', points: 4} ])); // [ 4, 6, 8 ] 复制代码

異なる機能ツールのネイティブデータ型を使用するためにファンクタ/高階関数を使用するという概念は、関数型プログラミング動作に重要です。同様の概念は、さまざまな方法のすべての種類に適用されます。

時間のリストは、流れの続きです。

あなただけの唯一の方法アレイを知っておく必要があり、および関数は、コンテナとコンテナの値には適用されません。例えば、アレイは、データの集合です。時間のリストは、流れの続きです - あなたは将来、関数型プログラミングでは、この実践の経験を持っています - あなたは、着信イベントストリームを処理するために同じツールの機能を使用することができます。

宣言型プログラミング命令型プログラミング&

関数型プログラミングは、宣言型プログラミングパラダイムで発現された場合、論理制御フローは、明示的に、記載されていません。

ラインによる命令型プログラミングコードの行は、所望の結果を達成するための特定の手順について説明します。フロー制御は、単純に何を気にしないのですか?

宣言型プログラミングの抽象化は、制御プロセスフローデータを行う方法流れを説明するためのコードを使用して、どのように抽象的な方法を取得します。

次の例では、マッピング命令型プログラミングデジタルアレイを示し、新しいアレイ2によって返された値を返します。

const doubleMap = numbers => {
  const doubled = [];
  for (let i = 0; i < numbers.length; i++) { doubled.push(numbers[i] * 2); } return doubled; }; console.log(doubleMap([2, 3, 4])); // [4, 6, 8] 复制代码

宣言型プログラミングは、機能・ツール使って同じことを行うが、これにArray.prototype.map()は、データ・ストリームのより明確な表現を加えることができ、抽象的フロー制御を:

const doubleMap = numbers => numbers.map(n => n * 2); console.log(doubleMap([2, 3, 4])); // [4, 6, 8] 复制代码

声明命令型プログラミングは、多くの場合、すなわち期間、使用されているなど、アクション文のコードを実行しforifswitchthrowなどが挙げられます。

宣言型のプログラミング・コードは、発現は、コードの一部が戻り値を持つ、より依存性発現です。以下の式の例:

2 * 2
doubleMap([2, 3, 4]) Math.max(4, 3, 2) 复制代码

あなたは、多くの場合、関数は、式を返すかの機能は、コードに渡され、式が変数に代入されて表示されます。

結論

この記事のポイント:

  • 機能共有機能ではなく、純粋な状態または副作用のを使用しています
  • 変数データではなく、不変性を引き継ぎます
  • 構図のフロー制御機能を使用すると、必須ではありません
  • 高階関数によるデータの多くの種類に適用することができ多重化することができ、多くのネイティブ、ユーティリティ機能だけでなく、指定されたデータ処理
  • 宣言型プログラミングではなく、命令型プログラミング(あなたが何をすべきか知っている、ない方法)
  • 式と文
  • 高次&血管コントラスト機能アドホック多型



おすすめ

転載: www.cnblogs.com/dashjunih/p/10988104.html
おすすめ