Как быстро начать работу с VUE3.0: Войдите в
Рекомендации по обучению: Учебное пособие по изучению JavaScript.
Я видел много объяснений по функциональному программированию, но большинство из них находятся на теоретическом уровне, а некоторые предназначены только для чисто функциональных языков программирования. например Хаскель. Цель этой статьи — рассказать о конкретной практике функционального программирования на JavaScript в моих глазах. Причина, по которой это «в моих глазах», заключается в том, что то, что я говорю, представляет собой лишь мое личное мнение, которое может противоречить некоторым строгим понятиям.
В этой статье опущено большое количество формальных представлений о концепциях, и основное внимание уделяется показу того, что такое функциональный код в JavaScript, в чем разница между функциональным кодом и обычным написанием, какие преимущества может принести нам функциональный код и что общего. Каковы некоторые функциональные модели?
Я думаю, что функциональное программирование можно понимать как метод программирования, в котором в качестве основного носителя используются функции. Использование функций для дизассемблирования и абстрагирования общих выражений
сравнивается с императивами. Основные моменты заключаются в следующем:
более четкая семантика, более высокая возможность повторного использования, более высокая удобство обслуживания, лучшее ограничение области действия и меньшее количество побочных эффектов. Следующий пример представляет собой конкретное функциональное выражение
кода Javascript
// каждое слово в массиве. первая буква // Общее написание const arr = ['apple', 'pen', 'apple-pen'] for(const i in arr){; const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1 }); console.log(обр); // Функциональный метод записи - function UpperFirst(word) { вернуть слово[0].toUpperCase() + word.slice(1 }); функция wordToUpperCase(arr) { вернуть arr.map(upperFirst); console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // Функциональный метод записи 2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1))) ;
Когда ситуация усложняется, способ написания выражений сталкивается с рядом проблем:
смысл неочевиден, его постепенно становится трудно поддерживать, возможность повторного использования плохая, будет генерироваться больше кода и много промежуточных переменных. Функциональное программирование хорошо решает вышеописанные проблемы. Во-первых, обратитесь к методу функционального написания 1, который использует инкапсуляцию функций для декомпозиции функций (детализация не уникальна), инкапсулирует их в различные функции, а затем использует комбинированные вызовы для достижения цели. Это делает выражение понятным и простым в обслуживании, повторном использовании и расширении. Во-вторых, используя функции высшего порядка, Array.map заменяет for...of для обхода массива, сокращая промежуточные переменные и операции.
Основное различие между методом функциональной записи 1 и методом функциональной записи 2 заключается в том, что вы можете решить, можно ли повторно использовать функцию позже. Если нет, то лучше использовать последний.
Из приведенного выше метода функционального написания 2 мы видим, что в процессе написания функционального кода легко вызвать горизонтальное расширение, то есть создать несколько уровней вложенности. Ниже приведем крайний пример.
Код Javascript
// Вычисление суммы чисел // Общий метод записи console.log(1 + 2 + 3 - 4) // Функциональная функция записи sum(a, b) { вернуть а + б } функция sub(a, b) { вернуть а-б } console.log(sub(sum(sum(1, 2), 3), 4); Этот пример показывает только крайний случай горизонтального расширения. Поскольку количество вложенных уровней функций продолжает увеличиваться, код станет менее читабельным. Производительность сильно снижается и легко допустить ошибку. В этом случае мы можем рассмотреть несколько методов оптимизации, например следующую оптимизацию цепочки. // Оптимизация написания (ну, вы правильно прочитали, это цепное написание lodash) Код Javascript const utils = { цепь(а) { this._temp = а; верните это; }, сумма (б) { this._temp += б; верните это; }, суб(б) { this._temp -= б; верните это; }, ценить() { const _temp = this._temp; this._temp = не определено; вернуть _temp; } }; console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
После переписывания таким образом общая структура станет понятнее, и каждое звено цепочки станет понятнее. будет Что делать, также можно легко показать. Еще одним хорошим примером сравнения вложенности и цепочки функций является функция обратного вызова и шаблон обещания.
Код Javascript
// Последовательный запрос двух интерфейсов // Функция обратного вызова import $ from 'jquery' $.post('a/url/to/target', (rs) => { если(р){ $.post('a/url/to/another/target', (rs2) => { если(rs2){ $.post('a/url/to/ Third/target'); } }); } }); // Запрос на импорт обещания от 'catta'; // catta — это легкий инструмент запроса, который поддерживает fetch, jsonp, ajax и не имеет зависимостей request('a/url/to/target') .then(rs => rs ? $.post('a/url/to/another/target') : Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/ Third/target') : Promise.reject());
По мере увеличения уровня вложенности функции обратного вызова и одноуровневой сложности она станет такой: раздутый и сложный в обслуживании, но цепная структура Promise все равно может расширяться по вертикали, когда сложность высока, а иерархическая изоляция очень очевидна.
Замыканиеобщей модели функционального программирования
может сохранять локальные переменные в блоке кода, который не освобождается.
Концепция замыкания относительно абстрактна. Я считаю, что все более или менее знают и используют эту функцию
. можно сумки принести нам?
Давайте сначала посмотрим, как создать замыкание:
Код Javascript
// Создаем функцию замыкания makeCounter() { пусть к = 0; функция возврата() { вернуть ++к; } } константный счетчик = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter Блок кода этой функции ссылается на локальную переменную k в возвращаемой функции, что приводит к сбою локальной переменной. функция выполняется, она перерабатывается системой, тем самым генерируя замыкание. Функция этого замыкания состоит в том, чтобы «сохранить» локальную переменную, чтобы переменную можно было повторно использовать при вызове внутренней функции; в отличие от глобальных переменных, на эту переменную можно ссылаться только внутри функции;
Другими словами, замыкания фактически создают некоторые «постоянные переменные», которые являются частными для функции.
Итак, из этого примера мы можем сделать вывод, что условия создания замыкания следующие:
существуют внутренние и внешние функции. Внутренняя функция ссылается на локальные переменные внешней функции. Основная цель замыкания — определить. некоторые функции, ограниченные постоянными переменными, эти переменные можно использовать для кэширования или промежуточных вычислений и т. д.
Код Javascript
// Простой инструмент кэширования // Анонимная функция создает замыкание const Cache = (function() { константное хранилище = {}; возвращаться { получить (ключ) { вернуть магазин[ключ]; }, набор (ключ, значение) { магазин [ключ] = значение; } } }()); кэш.set('a', 1); кэш.get('a'); // 1Приведенный
выше пример представляет собой реализацию простого инструмента кэширования. Анонимная функция создает замыкание, чтобы на объект хранилища можно было всегда ссылаться. , не будет перерабатываться.
Недостатки замыканий: постоянные переменные не будут освобождаться нормально и продолжать занимать пространство памяти, что может легко привести к ненужной трате памяти, поэтому обычно требуются некоторые дополнительные механизмы очистки вручную.
Функция, которая принимает или возвращает функцию, называется функцией высшего порядка.
Звучит как очень холодное слово, но на самом деле мы часто его используем, просто не знаем их названий. Язык JavaScript изначально поддерживает функции высшего порядка, поскольку функции JavaScript являются первоклассными гражданами и могут использоваться как в качестве параметров, так и в качестве возвращаемого значения другой функции.
В JavaScript часто можно увидеть множество встроенных функций высокого порядка, таких как Array.map, Array.reduce и Array.filter.
Давайте рассмотрим, как он использует
карту (отображение— это коллекция
Другими словами, каждый элемент в наборе подвергается одному и тому же преобразованию для создания новой
карты набора. Как функция высокого порядка, она принимает параметр функции в качестве логического
Javascript-кода
сопоставления// Добавьте по одному к каждому элементу в. массив для формирования нового массива // Общий метод записи const arr = [1,2,3]; const rs = []; for(const n of arr){ rs.push(++n); } console.log(rs) // карта перезаписывает const arr = [1,2,3]; const rs = arr.map(n => ++n);
приведенный выше общий метод записи, использование цикла for...of для обхода массива приведет к дополнительные операции, и существует риск изменения исходного массива
, но функция карты инкапсулирует необходимые операции, так что нам нужно заботиться только о реализации функции логики отображения, что уменьшает объем кода и риск побочных действий. эффекты.
дает некоторые параметры функции и генерирует новую функцию, которая принимает другие параметры.
Возможно, вы не часто слышите этот термин, но каждый, кто использовал undescore или lodash, видел его.
Существует волшебная функция _.partial, которая представляет собой каррированный
Javascript-код
// Получить относительный путь целевого файла к базовому пути // Общий метод записи — const BASE = '/path/to/base'; const относительный путь = относительный путь (BASE, '/some/path'); // _.parical rewrite const BASE = '/path/to/base'; constrelativeFromBase = _.partial(path.relative, BASE); const относительныйPath = относительныйFromBase('/some/path');
Через _.partial мы получаем новую функцию относительныйFromBase. Когда эта функция вызывается, это эквивалентно вызову path.relative, и первый параметр передается в BASE по умолчанию. . Последующие переданные параметры добавляются по порядку.
В этом случае мы действительно хотим каждый раз получать путь относительно BASE, а не относительно какого-либо пути. Каррирование позволяет нам заботиться только о некоторых параметрах функции, что делает назначение функции более ясным и упрощает ее вызов.
объединяет возможности нескольких функций для создания новой функции.
Возможно, вы впервые видели это в lodash, методе compose (теперь называемый потоком).
Javascript-код
// Напишите каждое слово в массиве с заглавной буквы, сделайте Base64. //Общий метод записи (один из них) const arr = ['pen', 'apple', 'applypen']; const rs = []; for(const w of arr){ rs.push(btoa(w.toUpperCase())) } console.log(rs); // _.flow rewrite const arr = ['pen', 'apple', 'applypen']; const UpperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow объединяет возможности функций преобразования верхнего регистра и преобразования Base64 для создания новой функции. Удобно использовать в качестве функции параметра или последующего повторного использования.
С моей точки зрения, мое понимание функционального программирования на JavaScript может отличаться от многих традиционных концепций. Я не только считаю, что функции высокого порядка считаются функциональным программированием. Другие, такие как комбинированные вызовы обычных функций, цепные структуры и т. д., я считаю, относятся к категории функционального программирования, если они используют функции в качестве основных. перевозчик.
И я думаю, что функциональное программирование не является необходимым и не должно быть обязательным правилом или требованием. Подобно объектно-ориентированному или другим идеям, это тоже один из способов. В большинстве случаев нам следует представлять собой комбинацию нескольких концепций, а не ограничиваться ими.
Рекомендации по теме: Учебное пособие по JavaScript.
Выше приведено подробное обсуждение функционального программирования на JavaScript. Для получения дополнительной информации обратите внимание на другие соответствующие статьи на китайском веб-сайте PHP!