JavaScriptのES6関数型プログラミング(A):閉鎖や高階関数

関数型プログラミングの歴史

第一の原則関数が小さくなって、第2の原理は、小さいことがある - ROBERT C. MARTIN

上記の文章を説明し、つまり、私たちはしばしばあると言う一つだけの機能など、:文字列と尾の文字の最初の文字が首都に変更され、我々は今、二つの機能を記述する必要があります。なぜ?より良い再利用するためには、造粒の機能を確保するために、より多くのをやって。

早くも1950年代のように、Lispの言語の作成を、関数型プログラミング(FPと呼ばれる関数型プログラミングは、)、既に全ての地平線に現れ始めていました。しかし、近年までは、そのエレガントな、シンプルな機能と機能は例外なく、減らし、より多くの機能特性(ラムダ式、マップのネイティブサポートとなり、プログラミングコミュニティ、リファレンスの設計における支配的な言語を通して人気の再開しました... ...)、Java8は、関数型プログラミングをサポートするために始めました。

フィールドのフロントエンドでは、我々はまた、関数型プログラミングの影の多くを見ることができます:Lodash.jsは、広く使われているRamda.jsライブラリ、ES6は矢印機能を追加、再来エルムは、フラックスの複雑さを減らすという思想を導入し、React16.6開始打ち上げReact.memo()、純粋な機能コンポーネントがメインプッシュフック16.8を開始することが可能となり、純粋な関数はアセンブリで記述された使用することをお勧めします......

これらの命令例外なく、関数型プログラミングこの古いプログラミングパラダイムは、年とその輝きが、それでも多くの活気とフェードしません。

関数型プログラミングとは何ですか

私たちは関数型プログラミングの歴史を理解する上で、それは素晴らしいことだことを確認してください。次に、我々は関数型プログラミングで何を見てするつもりですか?

実際には、私たちの小さな学校の機能は、一つの変数(のどのような機能をf(x) = 3x定義バイナリ機能......学術機能、収集との間のコレクションの説明である機能による)、変換関係、返す関数を介して入力される、唯一の出力値を。

そのため、この関数は実際に関係ある、またはマップの種類、およびこのマッピング関係は、私たちは別の関数の入力を一致させることができ、出力の機能の種類を知っていれば、それらを組み合わせることができることを、組み合わせることができます。

プログラミングの世界では、我々は実際に対処する必要がある唯一の「データ」と「関係」と「関係」は関数であり、「データ」の引数を渡すことがあります。彼らはただ探している私たちは、いわゆるプログラミングマッピング関係大文字に文字列の最初の文字:のような、。関係が見つかったら、問題が解決され、残りのものは、それを私たちに戻って別のデータに変換し、この関係を介してデータフローをさせることです。

製品としての原料入力、出力として、作業工程のラインのワークショップを想像するデータは、出力から入力の関数とすることができる別の機能に流入し続けることができ、その後、出力最終結果、ラインものということではないのですか?

だから今は明らかに関数型プログラミングは右、何ですか?実際には、関係を構築する方法の詳細焦点を置くために、プログラミングプロセスで強調されています。すべての問題を解決するための効率的なビルドパイプラインを構築することにより。むしろよりも前後にパスデータ内の異なるエネルギープラントに点在。

関数型プログラミング機能

  • 関数は、ファーストクラスの市民で

Wikipediaの概念によると、プログラミング言語のファーストクラスの市民は、英国のコンピュータ科学者クリストファー・ストレイチーによって提案されてくる、前世紀の60年代初期の時間は、その時には、パソコン、ないインターネット、ないブラウザ、なしがありませんでしたJavaScriptを。そして、明確な定義を与えていません。

ファーストクラスの市民について、私は正式な定義は本から来て見つけ、「プログラミング言語語用論」には、この本は、多くの大学の教科書のために設計されたプログラミング言語です。

一般的に、プログラミング言語の値は、それがパラメータとして渡すことができれば第一クラスのステータスを持っていると言われてサブルーチンから返された、または変数に割り当てられます。

換言すれば、プログラミング言語で、戻り値の関数としてのパラメータの関数として第一級市民は、変数に割り当てることができます。

たとえば、ほとんどすべてのプログラミング言語での文字列はファーストクラスの市民で、文字列は、関数パラメータとして使用することができ、文字列は、関数の戻り値として使用することができ、文字列は変数に割り当てることができます。

プログラミング言語のさまざまな機能は、必ずしもそのような前のバージョン8のJavaなどの第一級市民ではありません。

JavaScriptが、関数​​は、変数に割り当てることができ、それは関数のパラメータとしてだけでなく、関数の戻り値として使用し、したがってJavaScript関数にすることができる第一級市民です。

  • 宣言型プログラミング(宣言型プログラミング)

上記の例で見ることができ、関数型プログラミングではなく、それを行う方法よりも、私は何をする必要があるか文でほとんどの時間です。プログラミングのこのスタイルは、と呼ばれる宣言型プログラミングを

// 比如:我们要打印数组中的每个元素
// 1. 命令式编程
let arr = [1, 2, 3];
for (let i = 0, len = arr.length; i < len; i++) {
  console.log(arr[i])
}

// 2. 声明式编程
let arr = [1, 2, 3];
arr.forEach(item => {
  console.log(item)
})

/*
* 相对于命令式编程的 for 循环拿到每个元素,声明式编程不需要自己去找每个元素
* 因为 forEach 已经帮我们拿到了,就是 item,直接打印出来就行
*/

これは、ほとんどの宣言型のコードは自然言語に近いため、コードの可読性が、特に高いという利点を持っており、同時に、それは特定の実装を気にしないので、それは、人材の多くの解放であるので、それはする能力を最適化することができますまた、労働者の私たちの分裂を促進する特定の実装、。

  • 不活性は、(遅延評価)を行います

これは、必要なときにのみ実行いわゆる不活性実行機能、無意味すなわちない中間変数を指します。

  • ステートレスと不変データ(ステートレスおよび不変データ)

これは、関数型プログラミングのコア概念です:

不変データ:それはあなたがオブジェクトを変更したい場合は、あなたではなく、既存のオブジェクトを変更するよりも、修正するための新しいオブジェクトを作成する必要があることを意味し、すべてのデータが不変である必要があり。
ステートレス:関数の主な重点、あなたが実行するたびに、それは外部の状態変化の完全に独立し、同じ出力が得られ、同じ入力与え、あなたは同じことを最初に実行するときのようにする必要があります。

  • 副作用なし(副作用)

副作用は、一般的に公正な取り分も悪影響をもたらした後に行うものを指します。機能では、最も一般的な副作用は、外部変数を自由に変更できます渡されたのjsオブジェクトはアドレスへの参照ですので、バグを持参するのは非常に簡単です。

例えば、マップ機能は、新しい配列を生成するために、変換関数に従って入力配列の本質的な機能です。JSでは、私たちはしばしばループとしてマップ、マップを使用するには、この「過ち」の下に見ることができ、その後、直接配列内の値を変更します。

const list = [...];
// 修改 list 中的 type 和 age
list.map(item => {
  item.type = 1;
  item.age++;
})

参照モーメントはクール渡すと、リファクタリングの火葬場

この出力の主な機能がなくなっている、それはその副作用である、直接外部変数を変更となりました。言葉遣いは、副作用あってはなりません。

const list = [...];
// 修改 list 中的 type 和 age
const newList = list.map(item => ({...item, type: 1, age:item.age + 1}));

、関数は副作用を持たないことを確実にするために、データの不変性を確保するために、そして第二に、ため状態がもたらす共有の問題の多くを回避することができます。人は明白であるが、プロジェクトの反復で、プロジェクト参加者の数を増やし、そして我々はより多くの同じ変数への参照に依存しない場合がありますときに、コードを維持する場合、この問題はますます深刻になるだろう。ディフェンダーは最終的にも、自身が最後に変数を知らないかもしれところバグを生成するために変更されます。

  • 純粋関数(純粋関数)

関数型プログラミングが最も懸念しているオブジェクトは、純粋な機能と二つの純粋な関数の概念です。

外部の状態に依存しない(ステートレス):関数の演算結果が依存、グローバル変数、このポインタ、IO操作ではありません。
いいえ副作用(変更データ):グローバル変数を変更しないで、パラメータを変更しません。

だから、純粋な関数が真の意味での「機能」で、それはまた、参照透明性を次の- 同じ入力は常に同じ出力が得られます

だから私たちは純粋な機能の使用を強調し、純粋な機能の意味は何ですか?

テストし、最適化することが簡単:による純粋な関数に、この意味では、実際のプロジェクト開発に非常に大きな意味は常に同じ入力に対して同じ結果を返しますので、私たちは簡単に関数の結果と主張することができ、また、最適化機能は影響を与えないことを保証することができます他のコードを実行します。このアイデアは、このように生成されたコードは、しばしば、より堅牢で、テスト駆動開発TDD(テスト駆動開発)に沿ったものです。

キャッシュ可能性:同じ入力は常に同じ出力を返すことができますので、私たちは事前に、関数の結果をキャッシュすることができますので、多くのいわゆるmemoizeライブラリ関数、例をmemoizeするには、以下の単純化されたバージョンは、この機能が存在することができます結果キャッシュ機能は、この計算のためのフィボナッチのように、それは非常に良い緩衝効果を再生することができます。

  function memoize(fn) {
    const cache = {};
    return function() {
      const key = JSON.stringify(arguments);
      var value = cache[key];
      if(!value) {
        value = [fn.apply(null, arguments)];  // 放在一个数组中,方便应对 undefined,null 等异常情况
        cache[key] = value; 
      }
      return value[0];
    }
  }

  const fibonacci = memoize(n => n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2));
  console.log(fibonacci(4))  // 执行后缓存了 fibonacci(2), fibonacci(3),  fibonacci(4)
  console.log(fibonacci(10)) // fibonacci(2), fibonacci(3),  fibonacci(4) 的结果直接从缓存中取出,同时缓存其他的

クロージャ

定義:他の変数の内部機能を読み取ることができる機能、本質的に、プロセス変数を解析する(内側から)

クロージャはトピックのESと不可分でなく、理解するのが困難であり、徹底的な概念を理解する必要がありますです!クロージャといえば、私たちは、変数と変数のスコープのライフサイクルは密接にそれに関連付けられているに言及しなければならないでしょう。以下を見てください:

変数のスコープ

グローバルスコープとローカルスコープ:2つのカテゴリに分類され、変数のスコープ。

  • 代わって、変数を宣言するためにスクリプトタグや無用varキーワードで記述された変数のグローバル変数の任意の場所では、ページにアクセスすることができます
  • 機能では、あるキーワードvarで変数を宣言ローカル変数のみにアクセスするために、関数内で、ローカル変数を
function fn() {
    var a = 1;     // a为局部变量
    console.log(a);  // 1
}
fn();
console.log(a);     // a is not defined  外部访问不到内部的变量

上記のコードでは、関数の外へ出るのローカル変数の関数宣言を示しています。しかし、我々は関数の外にそれを取得したい、どのようにしますか?の閉鎖後パワープレーに依存します。

関数は、関数のスコープを作成することができ、この変数は関数内で宣言されていない場合は、変数を探している場合は、関数のスコープは、それが外側の層の機能を見ていきますが、グローバル変数は、これまでに発見されています。

だから、また、いわゆるスコープチェーンを形成し内側から変数を探します。

var a = 7;
function outer() {
    var b = 8;
    function inner() {
        var c = 9;
        alert(b);
        alert(a);
    }
    inner();
    alert(c);   // c is not defined
}
outer();    // 调用函数

または関数の先頭には、スコープチェーンを使用して、我々が取得しようと、FN機能の転換:

function fn() {
    var a = 1;     // a为局部变量
    return function() {
        console.log(a);
    }
}
var fn2 = fn();
fn2();      // 1

変数のスコープ、このチェーンに沿ってスコープを理解し、再び閉鎖の定義を見て:閉鎖機能は、(内側から)本質的解決のプロセス変数で、内部変数の他の機能を読み取る能力であります

変数のライフサイクル

変数のスコープを理解し、変数のライフサイクルを見て、簡単なポイントは、プログラムの中で生き残ることができるどのくらいです。

  • 我々は、手動でそれを破壊しない限り、グローバル変数の場合は、そのライフサイクルマシンのために、永久的であること(メモリのオーバーフローを防ぐためにも必要です)
  • varステートメントにより、関数内の変数の場合はそれほど幸運で、心配ではありません。関数が終了すると、それは時にゴミ出し廃棄物処理機構ブラウザ、続いても何の利用価値はありません
    :そのような次のコードのように、
var forever = 'i am forever exist'  // 全局变量,永生
function fn() {
    var a = 123;    // fn执行完毕后,变量a就将被销毁了
    console.log(a);
}
fn();

関数が終了すると、内部変数が破壊冷酷でした。その後、我々は変数を保存する方法がありませんか?閉鎖 - 答えはイエス、救世主が来ています

クロージャを作成します

function outFn() {
    var i = 1;
    function inFn () {
        return ++i
    }
    return inFn;
}
var fn = outFn(); // 此处创建了一个闭包
fn();   // 2
fn();   // 3
fn();   // 4

上記コードは、2つの特徴があり、クロージャを作成します。

  1. INFNの関数は、関数内にネストoutFn
  2. OutFn関数は内部関数INFNを返します。

実行後のvar fn = outFn();変数が実際に関数fnのINFNポインティングされた後に返され、次いで、FN()は、iの値(第1時間)を実行します。実際には、このコードは、関数outFn内の関数outFn FN機能INFN参照の外の変数ので、クロージャを作成します。変数はoutFn FN関数外で参照されたときに内部関数INFN機能outFn換言すれば、それはクロージャ(iはメモリに格納され、直ちに破棄されない変数の内部関数)を作成します。

参考リンク:
作成閉鎖の
閉鎖やメモリ

高次機能

定義:高階関数がある引数として受け付ける関数または出力として返す関数機能。

次の2つの状況高階関数を理解し、使用するのに役立ちますこれらの2つのシナリオを、明確に、説明します。

パラメータとしての機能

最も一般的なのパラメータとしての機能には、コールバック関数です。たとえば:AJAX非同期リクエストの際、コールバック関数は、非常に頻繁に使用されます。返すように非同期実行要求を決定することができないので、要求が完了するまで、コールバック関数コールバックの引数として、コールバック関数が実行さ。

$.ajax({
  url: 'http://musicapi.leanapp.cn/search',  // 以网易云音乐为例
  data: {
      keywords
  },
  success: function (res) {
      callback && callback(res.result.songs);
  }
})

戻り値出力の関数として、

シナリオの出力の戻り値としての機能があまりにも多くなり、それはまた、関数型プログラミングのアイデアを具現化しています。実際には、クロージャの例から、我々は高階関数についての関連する詳細を見ているだろう。

我々はデータの種類を決定するために行ったときに覚えている、我々は通過しているObject.prototype.toString計算するが、各データ間の入力'[object XXX]'だけで同じではありません。

ここでは、より高次の機能をパッケージ化し、変数の種類を達成するために決定されます。

function isType (type) {
    return function (obj) {
        return Object.prototype.toString.call(obj) === `[object ${type}]
    }
}

const isArray = isType('Array'); // 判断数组类型的函数
const isString = isType('String'); // 判断字符串类型的函数
console.log(isArray([1, 2]); // true
console.log(isString({});  // false

参考リンク:
高階関数、それ、あなたはどのように美しいです!
簡潔なJavaScriptの関数型プログラミング-初心者

概要

純粋関数、変数のスコープ、閉鎖、高階関数:このフォーカスを要約すると。

  1. ピュア定義関数:与えられた入力機能は、同じ出力を返します。
  2. 変数のスコープは、クロージャの本質です。アップ見つけるための変数のスコープの特性によると、閉鎖がメモリ変数にキャッシュすることができ、機能がすぐに破壊されることはありません完了します。
  3. 高階関数のコアは閉鎖され、将来的に使用されるいくつかの変数をキャッシュするクロージャの使用を達成することができカリー化、部分的なアプリケーション...

次のセクションでは、カリー化について説明使用し、部分的な組成物として...

おすすめ

転載: www.cnblogs.com/chenwenhao/p/11668894.html