- Оригинальный адрес:A Quick Introduction to Functional Javascript
- Оригинальный автор:Angelos Chalaris
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:Zheng7426
- Корректор:AmyFoxFN
функциональное программирование— одна из самых горячих тенденций прямо сейчас, и есть много веских аргументов в пользу того, почему люди хотят использовать ее в своем коде. Я не буду подробно описывать здесь все концепции и идеи функционального программирования, но постараюсь показать вам, как использовать его в повседневных ситуациях иJavaScriptКак использовать этот вид программирования при работе с .
Функциональное программирование — это парадигма программирования, которая рассматривает компьютерные операции как математические функциональные вычисления и избегает изменения состояния и неустойчивых данных.
Переопределенная функция
Прежде чем погрузиться в парадигму функционального программирования JavaScript, нам нужно знать, что такоеФункции высшего порядка, его назначение и значение самого определения. Функции более высокого порядка могут либо получать функции в качестве аргументов, либо выводить функции в качестве результатов. тебе нужно помнитьФункция также является значением, что означает, что вы можете передавать функции точно так же, как переменные.
Итак, в JavaScript вы можете сделать это:
// 创建函数
function f(x){
return x * x;
}
// 调用该函数
f(5); // 25
// 创建匿名函数
// 并赋值一个变量
var g = function(x){
return x * x;
}
// 传递函数
var h = g;
// And use it
h(5); // 25
Использовать функции в качестве значений
После того, как вы воспользуетесь описанным выше приемом, ваш код будет легче использовать повторно, и он станет более мощным. Мы все сталкивались с ситуациями, когда вы хотите передать функцию в другую функцию для выполнения задачи, но для этого вам нужно написать дополнительный код, верно? С функциональным программированием вам не нужно будет писать дополнительный код, и вы можете сделать свой код очень чистым и легким для понимания.
Следует отметить, что правильный функциональный код характеризуетсяникаких побочных эффектов, то есть функции должны зависеть только от входных аргументов и никак не влиять на внешнюю среду. Эта функция имеет важные последствия, например: если параметры, переданные в функцию, одинаковы, вывод всегда один и тот же; если вывод вызываемой функции не используется, то результатом будет Удаление, это не повлияет на другой код.
Встроенные методы, использующие прототипы массивов
Array.prototype
Это должно стать вашим первым шагом к изучению функционального программирования на JavaScript.преобразование массива, которые довольно часто встречаются в современных веб-приложениях.
Давайте посмотрим на этоArray.prototype.sort()
Метод был бы хорошим, потому что конвертация довольно простая. Как следует из названия, мы можем использовать этот метод дляСортировать массив..sort()
Принимает только один параметр (т.е. функцию для сравнения двух элементов). Если первый элемент предшествует второму элементу, результатом будет отрицательное значение. В противном случае возвращается положительное значение.
Сортировка звучит очень просто, но когда вам нужно отсортировать массив, который намного сложнее обычного числового массива, это может быть не так просто. В следующем примере у нас есть массив объектов, которые хранятся в фунтах (lbs) или килограммы (kg) — это единица веса, и нам нужно отсортировать веса этих людей в порядке возрастания. Код будет выглядеть так:
// 咱们这个比较函数的定义
var sortByWeight = function(x,y){
var xW = x.measurement == "kg" ? x.weight : x.weight * 0.453592;
var yW = y.measurement == "kg" ? y.weight : y.weight * 0.453592;
return xW > yW ? 1 : -1;
}
// 两组数据有细微差别
// 要根据体重来对它们进行排序
var firstList = [
{ name: "John", weight: 220, measurement: "lbs" },
{ name: "Kate", weight: 58, measurement: "kg" },
{ name: "Mike", weight: 137, measurement: "lbs" },
{ name: "Sophie", weight: 66, measurement: "kg" },
];
var secondList = [
{ name: "Margaret", weight: 161, measurement: "lbs", age: 51 },
{ name: "Bill", weight: 76, measurement: "kg", age: 62 },
{ name: "Jonathan", weight: 72, measurement: "kg", age: 43 },
{ name: "Richard", weight: 74, measurement: "kg", age: 29 },
];
// 用开头定义的函数
// 对两组数据进行排序
firstList.sort(sortByWeight); // Kate, Mike, Sophie, John
secondList.sort(sortByWeight); // Jonathan, Margaret, Richard, Bill
Примеры сортировки двух массивов с помощью функционального программирования
В приведенном выше примере вы можете ясно увидеть преимущества использования функций более высокого порядка: экономия места и времени, а также повышение удобочитаемости и повторного использования вашего кода. Если вы не собираетесь использовать.sort()
Чтобы написать его, вам пришлось бы написать еще два цикла и повторить большую часть логики. Откровенно говоря, это привело бы к более многословному, раздутому и менее понятному коду.
Часто ваши операции с массивами — это не просто сортировка. По моему опыту, в зависимости от свойствфильтровать массивочень распространено, и нет возможности сравниватьArray.prototype.filter()
более подходящий. Фильтровать массив несложно, потому что вы просто передаете функцию в качестве параметра, а для тех элементов, которые нужно отфильтровать, функция возвращаетfalse
. В противном случае функция вернетtrue
. Просто, не так ли? Давайте посмотрим на пример:
// 一群人的数组
var myFriends = [
{ name: "John", gender: "male" },
{ name: "Kate", gender: "female" },
{ name: "Mike", gender: "male" },
{ name: "Sophie", gender: "female" },
{ name: "Richard", gender: "male" },
{ name: "Keith", gender: "male" }
];
// 基于性别的简易过滤器
var isMale = function(x){
return x.gender == "male";
}
myFriends.filter(isMale); // John, Mike, Richard, Keith
Простой пример фильтрации
несмотря на то что.filter()
вернет все подходящие элементы в массиве, вы также можете использоватьArray.prototype.find()
Извлеките первый подходящий элемент в массиве или используйтеArray.prototype.findIndex()
для извлечения индекса первого совпадающего элемента в массиве. Точно так же вы можете использоватьArray.prototype.some()
чтобы проверить, соответствует ли хотя бы один элемент условию, или используйтеArray.prototype.every()
чтобы проверить, подходят ли все элементы. Эти методы могут оказаться весьма полезными в некоторых приложениях, поэтому давайте рассмотрим пример, который охватывает их все:
// 一组关于分数的数组
// 不是每一项都标注了人名
var highScores = [
{score: 237, name: "Jim"},
{score: 108, name: "Kit"},
{score: 91, name: "Rob"},
{score: 0},
{score: 0}
];
// 这些简单且能重复使用的函数
// 是用来查看每一项是否有名字
// 以及分数是否为正数
var hasName = function(x){
return typeof x['name'] !== 'undefined';
}
var hasNotName = function(x){
return !hasName(x);
}
var nonZeroHighScore = function(x){
return x.score != 0;
}
// 填充空白的名字,直到所有空白的名字都有“---”
while (!highScores.every(hasName)){
var highScore = highScores.find(hasNotName);
highScore.name = "---";
var highScoreIndex = highScores.findIndex(hasNotName);
highScores[highScoreIndex] = highScore;
}
// 检查非零的分数是否存在
// 并在 console 里输出
if (highScores.some(nonZeroHighScore))
console.log(highScores.filter(nonZeroHighScore));
else
console.log("No non-zero high scores!");
Используйте функциональное программирование для структурирования данных
В этот момент вы должны чувствовать себя немного более связно. Приведенный выше пример ясно показывает, как функции более высокого порядка могут избавить вас от большого количества повторяющегося и сложного для понимания кода. Этот пример прост, но вы также можете увидеть простоту кода, резко контрастирующую с тем, что вы написали бы без использования парадигмы функционального программирования.
Помимо сложной логики в приведенном выше примере, иногда нам просто нужноПреобразование массива в другой массив, и не нужно вносить столько изменений в данные в массиве. В настоящее времяArray.prototype.map()
Это удобно, мы можем использовать этот метод для преобразования объектов в массиве..map()
Не то же самое, что метод, используемый в предыдущем примере, разница в том, что функция более высокого порядка в качестве параметра возвращает объект, который может быть любым объектом, который вы хотите написать. Позвольте мне продемонстрировать на простом примере:
// 一个有 4 个对象的数组
var myFriends = [
{ name: "John", surname: "Smith", age: 52},
{ name: "Sarah", surname: "Smith", age: 49},
{ name: "Michael", surname: "Jones", age: 46},
{ name: "Garry", surname: "Thomas", age: 48}
];
// 一个简单的函数
// 用来把名和姓放在一起
var fullName = function(x){
return x.name + " " + x.surname;
}
myFriends.map(fullName);
// 应输出
// ["John Smith", "Sarah Smith", "Michael Jones", "Garry Thomas"]
Операции сопоставления объектов в массиве
Как видно из приведенного выше примера, после использования массива.map()
метод, легко получить массив, содержащий только нужные нам свойства. В этом примере нам нужны только объекты вname
а такжеsurname
Эти две строки строк, поэтому я использовал простое сопоставление (Примечание переводчика: то есть с использованием метода карты), чтобы создать другой массив, содержащий только строки, используя исходный массив, содержащий множество объектов. Картирование, вероятно, более распространено, чем вы думаете, и оно может быть мощным инструментом в кармане каждого веб-разработчика. Итак, если вы ничего не помните во всей этой статье, ничего страшного, но вы должны помнить, как использовать.map()
.
Последнее, но не менее важное, стоит отметить, чтоПреобразование массива общего назначениясерединаArray.prototype.reduce()
..reduce()
Он отличается от всех упомянутых выше методов тем, что его параметр является не только функцией высшего порядка, но и содержитаккумулятор. Сначала это может показаться запутанным, поэтому начните с примера, который поможет вам понять.reduce()
Основная концепция, стоящая за ним:
// 关于不同公司支出的数组
var oldExpenses = [
{ company: "BigCompany Co.", value: 1200.10},
{ company: "Pineapple Inc.", value: 3107.02},
{ company: "Office Supplies Inc.", value: 266.97}
];
var newExpenses = [
{ company: "Office Supplies Inc.", value: 108.11},
{ company: "Megasoft Co.", value: 1208.99}
];
// 简单的求和函数
var sumValues = function(sum, x){
return sum + x.value;
}
// 将第一个数组降为几个数值之和
var oldExpensesSum = oldExpenses.reduce(sumValues, 0.0);
// 将第二个数组降为几个数值之和
console.log(newExpenses.reduce(sumValues, oldExpensesSum)); // 5891.19
уменьшить массив до значения суммы
Для тех, кто когда-либо суммировал значения в массиве, не должно быть особенно сложно понять приведенный выше пример. Начнем с определения повторно используемой функции высшего порядка, которая суммирует все значения в массиве. После этого мы используем эту функцию для суммирования значений выплат в первом массиве и обрабатываем вычисленное значение как начальное значение, вместо того, чтобы накапливать значения выплат во втором массиве с нуля. Таким образом, окончательный результат представляет собой сумму значений выплат двух массивов.
Конечно,.reduce()
Вы можете делать гораздо больше, чем просто суммировать в массиве. Большинство других методов не могут решить эту проблемукомплексное преобразование, вы можете использовать.reduce()
Легко решается с помощью массива или аккумулятора объектов. Практический пример — преобразование массива со многими статьями, каждая статья имеет заголовок и несколько тегов. Исходный массив будет преобразован в массив тегов, каждый из которых содержит массив количества статей, использующих тег, и заголовков этих статей. Давайте посмотрим на код:
// 一个带有标签的文章的数组
var articles = [
{title: "Introduction to Javascript Scope", tags: [ "Javascript", "Variables", "Scope"]},
{title: "Javascript Closures", tags: [ "Javascript", "Variables", "Closures"]},
{title: "A Guide to PWAs", tags: [ "Javascript", "PWA"]},
{title: "Javascript Functional Programming Examples", tags: [ "Javascript", "Functional", "Function"]},
{title: "Why Javascript Closures are Important", tags: [ "Javascript", "Variables", "Closures"]},
];
// 一个能够将文章数组降为标签数组的函数
//
var tagView = function(accumulator, x){
// 针对文章的标签数组(原数组)里的每一个标签
x.tags.forEach(function(currentTag){
// 写一个函数看看标签是否匹配
var findCurrentTag = function(y) { return y.tag == currentTag; };
// 检查是否该标签已经出现在累积器数组
if (accumulator.some(findCurrentTag)){
// 找到标签并获得索引
var existingTag = accumulator.find(findCurrentTag);
var existingTagIndex = accumulator.findIndex(findCurrentTag);
// 更新使用该标签的文章数目,以及文章标题的列表
accumulator[existingTagIndex].count += 1;
accumulator[existingTagIndex].articles.push(x.title);
}
// 否则就在累积器数组中增添标签
else {
accumulator.push({tag: currentTag, count: 1, articles: [x.title]});
}
});
// 返回累积器数组
return accumulator;
}
// 转化原数组
articles.reduce(tagView,[]);
// 输出:
/*
[
{tag: "Javascript", count: 5, articles: [
"Introduction to Javascript Scope",
"Javascript Closures",
"A Guide to PWAs",
"Javascript Functional Programming Examples",
"Why Javascript Closures are Important"
]},
{tag: "Variables", count: 3, articles: [
"Introduction to Javascript Scope",
"Javascript Closures",
"Why Javascript Closures are Important"
]},
{tag: "Scope", count: 1, articles: [
"Introduction to Javascript Scope"
]},
{tag: "Closures", count: 2, articles: [
"Javascript Closures",
"Why Javascript Closures are Important"
]},
{tag: "PWA", count: 1, articles: [
"A Guide to PWAs"
]},
{tag: "Functional", count: 1, articles: [
"Javascript Functional Programming Examples"
]},
{tag: "Function", count: 1, articles: [
"Javascript Functional Programming Examples"
]}
]
*/
Используйте reduce() для выполнения сложного преобразования
Приведенный выше пример может показаться немного сложным, поэтому его нужно изучать поэтапно. Прежде всего, конечным результатом, который нам нужен, является массив, поэтому начальное значение аккумулятора становится[]
.然后,咱想要数组中的每一个对象都包含标签名、使用该标签的文章数目以及文章标题的列表。不但如此,每一个标签在数组中只能出现一次,所以咱必须用.some()
,.find()
а также.findIndex()
чтобы проверить, существует ли тег, а затем преобразовать объект существующего тега вместо добавления нового объекта.
Сложность в том, что мы не можем определить функцию для проверки существования каждого тега (иначе нам понадобилось бы 7 разных функций). Поэтому мы определяем функции более высокого порядка в цикле текущей метки, чтобы мы могли снова использовать функции более высокого порядка и избежать переписывания кода. Кстати, это можно сделать и с помощью Currying, но я не буду объяснять этот трюк в этой статье.
После того, как мы получим объект тега в массиве аккумулятора, нам нужно только увеличить количество статей, использующих тег, и добавить статьи под текущим тегом в его массив статей. Наконец, мы возвращаемся к аккумулятору, и все готово. Если вы внимательно прочитаете его, то обнаружите, что код не только очень короткий, но и простой для понимания. В этом же случае нефункциональный программный код будет выглядеть очень запутанным и значительно более многословным.
Эпилог
Функциональное программирование — одна из самых горячих тенденций сейчас, и на то есть веские причины. Это позволяет нам писать более чистый, компактный и более «скупой» код, не беспокоясь о побочных эффектах и изменениях состояния. JavaScript[Array.prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype)
Методы очень полезны во многих повседневных ситуациях и позволяют нам выполнять простые и сложные преобразования массивов без необходимости писать слишком много повторяющегося кода.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.