これまでに登場したサンプルコードを注意深く見てみると、ここにあるメソッドのいくつかは馴染みのないものであることがわかります。 それらは、map()、filter()、およびreduce()関数であり、どの言語の関数型プログラミングにとっても重要です。 これらを使用すると、ループやステートメントを使用せずに、よりクリーンなコードを作成できます。
map()、filter()、reduce() 関数は、関数型プログラマのツールセットの中核部分を形成します。このツールセットには、関数型アプローチの中心となる一連の純粋な高次関数が含まれています。実際、これらは純粋関数および高階関数の典型であり、関数を入力として受け取り、出力結果を返し、副作用は発生しません。
ただし、これらはブラウザーの ECMAScript 5.1 実装標準であり、配列でのみ機能します。呼び出されるたびに、新しい配列が作成されて返されますが、既存の配列は変更されません。これらは関数を入力として受け取り、多くの場合コールバック関数として無名関数を使用します。配列を反復処理し、関数を配列の各要素に適用します。
myArray = [1,2,3,4]; newArray = myArray.map(function(x) {return x*2}); console.log(myArray); // Output: [1,2,3,4] console.log(newArray); // Output: [2,4,6,8]
もう 1 つのポイントは、配列でのみ機能し、オブジェクトなどの他の反復可能なデータ構造では機能できないことです。心配しないでください。Underscore.js、Lazy.js、stream.js など、独自のより強力な map()、filter()、reduce() を実装するライブラリが多数あります。
コールバック
これまでにコールバックを使用したことがない場合、その概念は少し混乱するかもしれません。特に Javascript では、JavaScript には関数を宣言する方法がいくつか用意されています。
コールバック関数は、別の関数に渡して使用するために使用されます。これは、オブジェクトを渡すのと同じようにロジックを渡す方法です。
var myArray = [1,2,3]; function myCallback(x){return x+1}; console.log(myArray.map(myCallback));
より単純なタスクの場合は、匿名関数を使用できます:
console.log(myArray.map(function(x){return x+1}));
コールバックは関数型プログラミングで使用されるだけでなく、JavaScript で多くのことを行うことができます。ほんの一例として、jQuery の AJAX 呼び出しの callback() 関数を次に示します。
function myCallback(xhr) { console.log(xhr.status); return true; } $.ajax(myURI).done(myCallback);
ここでは関数の名前だけが使用されていることに注意してください。関数を呼び出しているのではなく、関数を渡しているためです。
のように記述するのは間違いです。
$.ajax(myURI).fail(myCallback(xhr)); // 或者 $.ajax(myURI).fail(myCallback());
関数を呼び出すとどうなるでしょうか?この例では、myCallback(xhr) が実行を試み、コンソールに「未定義」と表示され、true が返されます。 ajax() が呼び出しを完了すると、名前に基づいて見つかったコールバック関数が「true」となり、エラーが報告されます。
つまり、コールバック関数に渡すパラメーターを指定できない場合、コールバック関数に必要なパラメーターを渡すために ajax() 関数が必要な場合は、return 関数を匿名関数でラップできます。 🎜>
function myCallback(status) { console.log(status); return true; } $.ajax(myURI).done(function(xhr) { myCallback(xhr.status) });
Array.prototype.map()
map() はこれらの関数のリーダーであり、配列内の要素にコールバック関数を適用するだけです。構文: arr.map(callback [, thisArg]);
パラメータ:
•callback(): この関数は、新しい配列の要素を生成します。受け取るパラメータは次のとおりです。 ◦ currentValue: 配列
内で現在スキャンされている要素。
◦index: 配列
内の現在の要素番号
◦array: 現在処理中の配列
として機能します。
例:
var integers = [1, -0, 9, -8, 3], numbers = [1, 2, 3, 4], str = 'hello world how ya doing?'; // 将整数映射为他们自己的绝对值 console.log(integers.map(Math.abs)); // 将数组中的元素与自己的位置序数相乘 console.log(numbers.map(function(x, i) { return x * i })); // 单词隔一个变一个大写 console.log(str.split(' ').map(function(s, i) { if (i % 2 == 0) return s.toUpperCase(); else return s; }));
MyObject.prototype.map = function(f) {  return new MyObject(f(this.value)); };
Array.prototype.filter()
filter() 関数は、配列内の一部の要素をフィルターで除外するために使用されます。コールバック関数は true (新しい配列に保持) または false (破棄) を返す必要があります。 map() を使用すると、同様のことを行うことができます。つまり、破棄したい要素に対して null を null として返します。ただし、filter() 関数は、新しい配列内の不要な要素を null で占有したままにするのではなく、削除します。位置。構文: arr.filter(callback [, thisArg]);
•callback(): この関数は配列内の各要素をテストするために使用され、true を返す必要があります。それ以外の場合は false を返します。これらのパラメータがあります: ◦ currentValue: 配列
内で現在走査されている要素
◦index: 配列
内の現在の要素の序数
◦array: 現在処理中の配列
として機能します。
例:
var myarray = [1, 2, 3, 4] words = 'hello 123 world how 345 ya doing'.split(' '); re = '[a-zA-Z]'; // 筛选整数 console.log([-2, -1, 0, 1, 2].filter(function(x) { return x > 0 })); // 筛选所有含字母的单词 console.log(words.filter(function(s) { return s.match(re); })); // 随机移除数组中的元素 console.log(myarray.filter(function() { return Math.floor(Math.random() * 2) }));
Array.prototype.reduce()
reduce()函数,有时也称为fold,它用于把数组中的所有值聚集到一起。回调需要返回组合对象的逻辑。 对于数字来说,它们往往会被加到一起或者乘到一起。对于字符串来说,它们往往是被追加到一起。
语法:arr.reduce(callback [, initialValue]);
参数
•callback():此函数把两个对象合并成一个对象,并将其返回。参数有: ◦previousValue:上一次回调函数被调用时返回的值,或者是初始值(如果有的话)
◦currentValue:数组当前正在处理的元素
◦index:数组中当前元素的序数
◦array:当前正在处理的数组
•initialValue:可选。第一次回调所传入参数的初始值
例子
var numbers = [1, 2, 3, 4]; // 把数组中所有的值加起来 console.log([1, 2, 3, 4, 5].reduce(function(x, y) { return x + y }, 0)); // 查找数组中最大的值 console.log(numbers.reduce(function(a, b) { return Math.max(a, b) // max()函数只能有两个参数 }) );
其它函数
map()、filter()和reduce()函数在我们辅助函数的工具箱里并不孤单。这里还有更多的函数几乎在所有函数式应用里都会被使用。
Array.prototype.forEach
forEach()函数本质上是map()函数的非纯版本,它会遍历整个数组,并对每个元素应用回调。 然而这些回调函数不返回值。它是实现for循环的一个更纯粹的方式。
语法:arr.forEach(callback [, thisArg]);
参数:
•callback():对数组中每一个元素所应用的。参数有: ◦currentValue:数组中当前正在处理的元素
◦index:数组中当前元素的序数
◦array:正在处理的数组
•thisArg:可选。回调函数中作为this的值
例子:
var arr = [1, 2, 3]; var nodes = arr.map(function(x) { var elem = document.createElement("div"); elem.textContent = x; return elem; }); // 对每一个元素的值输出日志 arr.forEach(function(x) { console.log(x) }); // 把节点追加到DOM上 nodes.forEach(function(x) { document.body.appendChild(x) });
Array.prototype.concat
如果不用for或while处理数组,你会经常需要把数组拼接起来。另一个Javascript内建函数concat就是专门干这事儿的。 concat函数会返回一个新数组但不改变旧数组。它可以把你传入的所有参数拼接到一起。
console.log([1, 2, 3].concat(['a','b','c']) // 拼接两个数组
// Output: [1, 2, 3, 'a','b','c']
它返回两个数组拼接成的数组,同时原来的那些数组没有被改变。这就意味着concat函数可以链式调用。
var arr1 = [1,2,3]; var arr2 = [4,5,6]; var arr3 = [7,8,9]; var x = arr1.concat(arr2, arr3); var y = arr1.concat(arr2).concat(arr3)); var z = arr1.concat(arr2.concat(arr3))); console.log(x); console.log(y); console.log(z);
变量x、y、z的值最后都是[1,2,3,4,5,6,7,8,9]。
Array.prototype.reverse
这个Javascript内建函数是用于数组变形的。reverse函数用于将一个数组反转,也就是第个一元素会跑到最后, 而最后一个元素变成了第一个元素。
然而,这个函数并不会返回一个新的数组,而是把原来的数组替换掉了。我们可以做个更好的。下面是一个纯的反转数组函数
var invert = function(arr) { return arr.map(function(x, i, a) { return a[a.length - (i + 1)]; }); }; var q = invert([1, 2, 3, 4]); console.log(q);
Array.prototype.sort
与map()、filter()和reduce()函数相似,排序函数sort()需要传入一个回调函数来定义数组如何排序。 但是,跟reverse()一样,它也会把原来的数组替换。这可不太好。
arr = [200, 12, 56, 7, 344];
console.log(arr.sort(function(a,b){return a–b}) );
// arr现在是: [7, 12, 56, 200, 344];
我们可以写一个纯函数的sort(),但是排序算法的源代码很麻烦。对于特别大的数组,应当根据特定的数据结构来选用适合的算法, 比如快速排序、合并排序、冒泡排序等等。
Array.prototype.every 和 Array.prototype.some
Array.prototype.every() 和 Array.prototype.some() 都是纯的高阶函数,它们是Array对象的方法, 通过回调函数根据数组各元素返回的布尔值(或相当于布尔的值)来进行测试。如果数组中所有的元素通过回调函数计算都返回True, every()函数就返回true;如果数组中有一个元素返回True,some()函数就返回True。
例子:
function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } console.log([1, 2, 3, 4].every(isNumber)); // Return: true console.log([1, 2, 'a'].every(isNumber)); // Return: false console.log([1, 2, 'a'].some(isNumber)); // Return: true