JavaScript中的函数式编程二(翻译)

《JavaScript中的函数式编程二(翻译)》

tips

原文链接: http://jrsinclair.com/articles/2016/gentle-introduction-to-functional-javascript-arrays/;
原文作者: James Sinclair;

JavaScript 函数式编程

这篇文章是引见函数式编程的四篇文章中的第二篇。在上一篇文章中,我们看到怎样运用函数使代码笼统地更简约,在这篇文章中我们将运用这个手艺在列表上。

处置惩罚函数和鸠合

会想之前的文章,我们议论了 DRY 原则。我们看到用函数绑定一系列反复的操纵是很有效的。然则假如我们反复同一个函数很屡次呢?就像如许:

function addColour(colour) {
    var rainbowEl = document.getElementById('rainbow');
    var div = document.createElement('div');
    div.style.paddingTop = '10px';
    div.style.backgroundColour = colour;
    rainbowEl.appendChild(div);
}

addColour('red');
addColour('orange');
addColour('yellow');
addColour('green');
addColour('blue');
addColour('purple');

在这里 addColour 这个函数被挪用很屡次,我们依然反复了我们自身,这是我们一向想逃避的。一种重构的要领是构建一个数组包含这些色彩的列表,然后轮回挪用 addColour 这个函数。

var colours = [
    'red', 'orange', 'yellow',
    'green', 'blue', 'purple'
];

for (var i = 0; i < colours.length; i = i + 1) {
    addColour(colours[i]);
}

For-Each

JavaScript 许可我们把一个函数坐位参数传递给另一个函数,编写一个 forEach 函数是相称明白了:

function forEach(callback, array) {
    for (var i = 0; i < array.length; i = i + 1) {
        callback(array[i], i);
    }
}

这个函数接收实行 callback 函数,而且把数组的每一项作为参数挪用 callback 函数。
如今,在我们的例子中,我们想要对数组的每一个元素去运转 addColour 要领,运用我们的 forEach 我们仅需一行就能够圆满实行~

forEach(addColour, colours);

对数组中的每一个元素挪用一个要领黑白常有效的一个东西,JavaScript 也完成了这么一个特征构成,作为数组对象的一个要领。因而我们也能够运用它来替代我们的 forEach 要领,以下:

var colours = [
    'red', 'orange', 'yellow',
    'green', 'blue', 'purple'
];
colours.forEach(addColour);

我们能够查找更多的要领在 MDN 的 JavaScript 参考文档里

Map

我们的 forEach 已异常好用了,然则扔有一些局限性。假如回调函数返回一个值,那 forEach 只会疏忽掉这个返回值。我们能够恰当革新一下 forEach 函数,不管他返回什么范例的值我们都能够取得。我们能够取得一个数组,它包含了我们原始数字相对应的一些值。
让我们看看下面的例子,我们有一个 ID 的数组,然后我们想取得他们每一个相对应的 DOM 元素鸠合。在程序上找到处理的要领,我们能够运用轮回:

var ids = ['unicorn', 'fairy', 'kitten'];
var elements = [];
for (var i = 0; i < ids.length; i = i + 1) {
    elements[i] = document.getElementById(ids[i]);
}
// elements now contains the elements we are after

我们想要说明盘算机是怎样建立一个索引变量而且增添它--细节我们不需多斟酌。让我们运用轮回就像 forEach 内里一样,而且把它复制给一个叫做 map 的变量。

var map = function(callback, array) {
    var newArray = [];
    for (var i = 0; i < array.length; i = i + 1) {
        newArray[i] = callback(array[i], i);
    }
    return newArray;
}

如今我们有了一个 map 函数,能够这么实用它:

var getElement = function(id) {
  return document.getElementById(id);
};

var elements = map(getElement, ids);

这个 map 函数小而简朴,然则它内里实行了另一个我们的超等函数,只需在数组的一个进口挪用它一次就能够够了,成倍的增长了函数的效力。
像 forEach 函数一样,JavaScript 自身也完成了 map 函数,作为数组对象的一个要领。我们能够挪用这部份要领以下:

var ids = ['unicorn', 'fairy', 'kitten'];
var getElement = function(id) {
  return document.getElementById(id);
};
var elements = ids.map(getElement, ids);

我们能够在 MDN 上查阅更多关于 map 函数的构成。

Reduce

嗯,map 函数事很有效的。然则我们想完成一个更有效的函数,假如我们对一切的数组元素实行函数然则返回仅仅一个值。这听起来可能有一点失常,函数返回一个值为何会比返回多个值更有效?为了寻觅答案,我们能够先看看下面这个函数是怎样事变的。
为了诠释,我们斟酌两个类似的题目:

  • 给一个数组一些数值元素,而且盘算他们的和;

  • 给一个数组一些字符串元素,而且用空格符在他们直接连接起来。
    如今我们来看看一个愚昧、眇乎小哉的例子--事实是他们也确切云云。这对于我来讲相称难忍耐,然则假如我们一旦进修到 reduce 函数是怎样事变的,我们就能够够把它应用在许多风趣的情况下。

我们再来看看程序上时如可处理这个题目的,照样轮回:

// Given an array of numbers, calculate the sum
var numbers = [1, 3, 5, 7, 9];
var total = 0;
for (i = 0; i < numbers.length; i = i + 1) {
    total = total + numbers[i];
}
// total is 25

// Given an array of words, join them together with a space between each word.
var words = ['sparkle', 'fairies', 'are', 'amazing'];
var sentence = '';
for (i = 0; i < words.length; i++) {
    sentence = sentence + ' ' + words[i];
}
// ' sparkle fairies are amazing'

两个计划都是一样的实在,他们都运用for轮回去迭代,他们都有个实行变量(total 和 sentence),他们都对实行变量赋值一个初始值。
让我们把他们轮回中的操纵抽出来写一个函数:

var add = function(a, b) {
    return a + b;
}

// Given an array of numbers, calculate the sum
var numbers = [1, 3, 5, 7, 9];
var total = 0;
for (i = 0; i < numbers.length; i = i + 1) {
    total = add(total, numbers[i]);
}
// total is 25

function joinWord(sentence, word) {
    return sentence + ' ' + word;
}

// Given an array of words, join them together with a space between each word.
var words = ['sparkle', 'fairies', 'are', 'amazing'];
var sentence = '';
for (i = 0; i < words.length; i++) {
    sentence = joinWord(sentence, words[i]);
}
// 'sparkle fairies are amazing'

如今,它变得简约有效了,内部函数把实行变量作为他的第一个参数,然后当前遍历到的数组元素值作为第二个参数。如今我们让它越发简约,我们把缭乱的 for 轮回放到函数内里。

var reduce = function(callback, initialValue, array) {
    var working = initialValue;
    for (var i = 0; i < array.length; i = i + 1) {
        working = callback(working, array[i]);
    }
    return working;
};

如今我们又个闪闪发亮的 reduce 新函数了,让我们来运用运用它:

var total = reduce(add, 0, numbers);
var sentence = reduce(joinWord, '', words);

就像 forEach 和 map 函数,reduce 函数也是 JavaScript 数组对象的规范要领之一。
我们能够这么运用它:

var total = numbers.reduce(add, 0);
var sentence = words.reduce(joinWord, '');

我们能够在 MDN 上查阅更多关于 reduce 函数的构成。

总结

正如我们前面所说起,他们都是很简朴的例子--add 和 joinWord 函数都异常地简朴。更小,更简朴的函数易于思索和测试。当我们把两个小而简朴的函数结合起来的时刻(就像 add 和 reduce),他的效果比起编写一个大而庞杂的函数来讲依然很好诠释。
然则,正如我前面所说,我们能够把许多要领结合起来做一些更有意义的事变。
让我们尝试更庞杂一些,我们把一个数据对象用 map 和 reduce 函数转化成一个 HTML 列表,数据以下:

var ponies = [
    [
        ['name', 'Fluttershy'],
        ['image', 'http://tinyurl.com/gpbnlf6'],
        ['description', 'Fluttershy is a female Pegasus pony and one of the main characters of My Little Pony Friendship is Magic.']
    ],
    [
        ['name', 'Applejack'],
        ['image', 'http://tinyurl.com/gkur8a6'],
        ['description', 'Applejack is a female Earth pony and one of the main characters of My Little Pony Friendship is Magic.']
    ],
    [
        ['name', 'Twilight Sparkle'],
        ['image', 'http://tinyurl.com/hj877vs'],
        ['description', 'Twilight Sparkle is the primary main character of My Little Pony Friendship is Magic.']
    ]
];

这些数据不是非常整齐,假如把内里的数组变成对象会更清楚。不过,在之前我们能够 reduce 函数去预算一些简朴的值就像数字或许字符串,然则没人敢说 reduce 返回的值肯定简朴。我们能够把它运用在 对象,数组 或许以至是 DOM 元素 上面。让我们制造一个函数把内部的数组(像 [‘name’, ‘Fluttershy’])以键值对的情势添加到一个对象当中。

var addToObject = function(obj, arr) {
    obj[arr[0]] = arr[1];
    return obj;
};

运用 addToObject 函数,我们能够把每一个数组转化成对象:

var ponyArrayToObject = function(ponyArray) {
    return reduce(addToObject, {}, ponyArray);
};

然后运用 map 函数把全部数组转化的更简约清楚:

var tidyPonies = map(ponyArrayToObject, ponies);

如今我们有了一个对象构成的数组,能够从Thomas Fuchs’ tweet-sized templating engine取得一些协助,我们能够再运用 reduce 函数把它转换成 HTML 片断。模版函数接收一个模版字符串和一个对象,他会把字符串当中 mustache-wrapped 花样的部份(像, {name} 或许 {image}))用对象中包含的属性值类替代。比方:

var data = { name: "Fluttershy" };
t("Hello {name}!", data);
// "Hello Fluttershy!"

data = { who: "Fluttershy", time: Date.now() };
t("Hello {name}! It's {time} ms since epoch.", data);
// "Hello Fluttershy! It's 1454135887369 ms since epoch."

所以,假如我们想要吧对象转换成列表项,我们能够这么做:

var ponyToListItem = function(pony) {
    var template = '<li><img src="{image}" alt="{name}"/>' +
                   '<div><h3>{name}</h3><p>{description}</p>' +
                   '</div></li>';
    return t(template, pony);
};

在上面我们把对象转换成了 html 片断,然则假如想转换全部数组,我们就须要 reduce 和 joinWord 要领:

var ponyList = map(ponyToListItem, tidyPonies);
var html = '<ul>' + reduce(joinWord, '', ponyList) + '</ul>';

我们能够在http://jsbin.com/wuzini/edit?html,js,output看到全部运转效果。

当你明白 reduce 和 map 要领以后,你就不再须要老旧的 for 轮回了。事实上,假如你决议在你的下一个项目上完整不运用 for 轮回将会是一个很有意义的应战。当你运用 reduce 和 map 愈来愈多的时刻,你就会注重另有没有更多的部份能够被笼统出来。一些罕见的包含过滤filtering,或许plucking。这些部份被运用的愈来愈频仍,人们把他们放到一个函数式编程的库内里,有一些盛行的库包含:

-----------------

<br/>
未亡待续…
[浏览下一节~]

原文地点
迎接关注blog~

    原文作者:阿城
    原文地址: https://segmentfault.com/a/1190000004544739
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞