Unsplashの メーカーによるNESAによる写真
JavaScriptプログラミングを学習すると、おそらく高階関数の概念に出くわしたことでしょう。 この概念は恐ろしいように見えるかもしれませんが、実際にはそれほど複雑ではありません。
JavaScriptは高階関数を操作できるため、関数型プログラミングに適しています。
JavaScriptでは高階関数が一般的です。 彼としばらく一緒に仕事をしていたら、気づかないうちにそのような機能を使った可能性が非常に高いです。
高階関数の全体像を把握するには、まず関数型プログラミングと一流の関数が何であるかを理解する必要があります。
ヒント :JavaScript関数を再利用すると、重複することがよくあります。 これを回避するには、 Bit(GitHub)を使用します。 新しい機能を簡単に見つけて共有し、最小限の管理変更で再適用できます。 それを試して、恥ずかしがらないでください。
関数型プログラミングとは
詳細に入ることなく、関数型プログラミングとは、一部の関数を引数として他の関数に渡し、3番目の関数を値として返すプログラミングです。 関数型プログラミングでは、関数で考えて操作します。
関数型プログラミングは、JavaScript、Haskell、Clojure、Scala、Erlangなどの言語で行われます。
ファーストクラスの機能とは
JavaScriptを使用することを学ぶとき、彼にとって関数は一流の市民であると聞いたことがあるでしょう。 実際、JavaScriptでは、他の関数型プログラミング言語と同様に、関数はオブジェクトです。
特にJavaScriptの場合、関数は特別な種類のオブジェクト(
Function
オブジェクト、またはファンクター)です。 例:
function greeting() { console.log('Hello World'); } // Invoking the function greeting(); // prints 'Hello World'
JavaScriptの関数がオブジェクトであることを示すために、次のようなことができます。
// We can add properties to functions like we do with objects greeting.lang = 'English'; // Prints 'English' console.log(greeting.lang);
注:上記はJavaScriptでは正常に機能しますが、乱用しないでください。 ファンクタにランダムなプロパティを割り当てることはできません。通常のオブジェクトを使用する方が良いでしょう。
JavaScriptでは、オブジェクト、文字列、数値など、他のエンティティでできることはすべて関数に適用されます。 他の関数への引数として(それらはコールバック関数またはコールバック関数と呼ばれます)、変数に割り当てるなど、転送できます。 これがJavaScript関数がファーストクラス関数と呼ばれる理由です。
関数を変数に割り当てる
JavaScriptを使用すると、関数を変数に割り当てることができます。 例:
const square = function(x) { return x * x; } // prints 25 square(5);
転送することもできます。 例:
const foo = square; // prints 36 foo(6);
関数を引数として渡す
関数を引数として他の関数に渡すことができます。 例:
function formalGreeting() { console.log("How are you?"); } function casualGreeting() { console.log("What's up?"); } function greet(type, greetFormal, greetCasual) { if(type === 'formal') { greetFormal(); } else if(type === 'casual') { greetCasual(); } } // prints 'What's up?' greet('casual', formalGreeting, casualGreeting);
それでは、最初のクラスの関数が何であるかを知ったので、JavaScriptの高階関数に移りましょう。
高階関数
高階関数とは、他の関数と連動する関数であり、関数を引数として使用するか、結果として関数を返します。
すでに言語に組み込まれている高階関数の例は、
Array.prototype.filter
および
Array.prototype.reduce
です。
実行中の高階関数
高階組み込み関数の例をいくつか見て、高階関数が使用されていないソリューションと比較してみましょう。
Array.prototype.map
map()
メソッドは、初期配列の各要素に対して渡された関数を呼び出した結果で新しい配列を作成します。
map()
メソッドは、コールバック関数の各値を受け取り、これらの値を使用して新しい配列を作成します。
map()
メソッドに渡されるコールバック関数は、
element
、
index
および
array
3つの引数を取ります。
これをいくつかの例で検討してください。
例1
数値の配列があり、そこから初期配列の各数値が2倍になる新しい配列を作成するとします。 高次関数がある場合とない場合に、この問題をどのように解決できますか?
高階関数なし:
const arr1 = [1, 2, 3]; const arr2 = []; for(let i = 0; i < arr1.length; i++) { arr2.push(arr1[i] * 2); } // prints [ 2, 4, 6 ] console.log(arr2);
高次
map
関数を使用する:
const arr1 = [1, 2, 3]; const arr2 = arr1.map(function(item) { return item * 2; }); console.log(arr2);
矢印関数を使用して、コードをさらに短くすることができます。
const arr1 = [1, 2, 3]; const arr2 = arr1.map(item => item * 2); console.log(arr2);
例2
複数の人の誕生日を含む配列があり、そこから年齢が変わる新しい配列を作成するとします。
高階関数なし:
const birthYear = [1975, 1997, 2002, 1995, 1985]; const ages = []; for(let i = 0; i < birthYear.length; i++) { let age = 2018 - birthYear[i]; ages.push(age); } // prints [ 43, 21, 16, 23, 33 ] console.log(ages);
高次
map
関数を使用する:
const birthYear = [1975, 1997, 2002, 1995, 1985]; const ages = birthYear.map(year => 2018 - year); // prints [ 43, 21, 16, 23, 33 ] console.log(ages);
Array.prototype.filter
filter()
メソッドは、渡された関数で指定されたテストに合格したすべての要素を含む新しい配列を作成します。
filter()
渡されるコールバック関数は、
element
、
index
および
array
3つの引数を取ります。
これをいくつかの例で検討してください。
例1
プロパティ「name」と「age」を持つオブジェクトを含む配列があると想像してください。 それから、成人(18歳以上)のみが示される新しい配列を作成します。
高階関数なし:
const persons = [ { name: 'Peter', age: 16 }, { name: 'Mark', age: 18 }, { name: 'John', age: 27 }, { name: 'Jane', age: 14 }, { name: 'Tony', age: 24}, ]; const fullAge = []; for(let i = 0; i < persons.length; i++) { if(persons[i].age >= 18) { fullAge.push(persons[i]); } } console.log(fullAge);
高次
filter
関数を使用する:
const persons = [ { name: 'Peter', age: 16 }, { name: 'Mark', age: 18 }, { name: 'John', age: 27 }, { name: 'Jane', age: 14 }, { name: 'Tony', age: 24}, ]; const fullAge = persons.filter(person => person.age >= 18); console.log(fullAge);
Array.prototype.reduce
reduce
メソッドは、配列の各値に関数を適用し、単一の値に減らします。 reduceメソッドは2つの引数を取ります:
- 処理されるコールバック関数。
- オプションの
initialValue
パラメーター(最初の関数呼び出しの最初の引数)。
コールバック関数は、
accumulator
、
currentValue
、
currentIndex
、
sourceArray
4つの引数を取ります。
initialValue
パラメーターが渡された場合、
accumulator
引数は
initialValue
引数と等しくなり、
currentValue
引数
currentValue
配列の最初の要素になります。
initialValue
パラメーターが渡されなかった場合、
accumulator
引数は配列の最初の要素と等しくなり、配列の2番目の要素は
currentValue
引数として取得されます。
例1
配列内の数値の合計を見つける必要があるとします。
高次の
reduce
関数を使用する:
const arr = [5, 7, 1, 8, 4]; const sum = arr.reduce(function(accumulator, currentValue) { return accumulator + currentValue; }); // prints 25 console.log(sum);
コールバック関数が配列の各値に適用されるたびに、
accumulator
引数は関数によって返された前のアクションの結果を保存し、
currentValue
は配列の次の値を取得します。 完了すると、結果は変数
sum
格納されます。
さらに、この関数に初期値を渡すことができます。
const arr = [5, 7, 1, 8, 4]; const sum = arr.reduce(function(accumulator, currentValue) { return accumulator + currentValue; }, 10); // prints 35 console.log(sum);
高階関数なし:
const arr = [5, 7, 1, 8, 4]; let sum = 0; for(let i = 0; i < arr.length; i++) { sum = sum + arr[i]; } // prints 25 console.log(sum);
ご覧のとおり、高階関数を使用すると、コードをより正確に、より短く、より容量的にすることができます。
独自の高次関数を作成する
ここまで、言語に組み込まれているさまざまな高階関数を調べてきました。 独自の高次関数を作成します。
JavaScriptが独自の
map
メソッドを持たないことを想像してください。 独自に構築することで、より高次の独自の機能を作成できます。
文字列の配列があり、そこから各要素が初期配列からの文字列の長さを表す積分配列を作成するとします。
const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C']; function mapForEach(arr, fn) { const newArray = []; for(let i = 0; i < arr.length; i++) { newArray.push( fn(arr[i]) ); } return newArray; } const lenArray = mapForEach(strArray, function(item) { return item.length; }); // prints [ 10, 6, 3, 4, 1 ] console.log(lenArray);
上記の例では、配列とコールバック関数
fn
を引数として取る高次関数
mapForEach
を作成しました。 この関数は、配列の各要素に周期的に適用され、各反復で
newArray.push
関数
newArray.push
一部として
fn
コールバック関数を
newArray.push
ます。
fn
コールバック関数は、初期配列の現在の要素を取得し、この要素の長さの値を返します。この値は、新しい
newArray
内に格納されます。 反復が完了すると、
newArray
配列
newArray
結果として返され、
lenArray
配列に割り当てられます。
おわりに
高階関数とは何かを学び、言語に組み込まれている関数のいくつかを調べました。 また、独自の高階関数を作成する方法も学びました。
つまり、高階関数は通常の関数のように機能しますが、他の関数を引数として受け取り、結果として返す機能が追加されています。
実際、それがすべてです。 この記事がお役に立てば、 MediumおよびTwitterでフォローしてください。 ご質問がある場合は、お気軽にコメントしてください! 喜んでお手伝いします。 :)