предисловие
Эта серия статей научит вас, как разработать собственную библиотеку инструментов на основе реального боевого опыта одной из ваших собственных библиотек инструментов разработки (ступенчатые ямы).Здесь вы можете изучить спецификации использования Git, базовое построение проекта и написание кода. Спецификации, идеи функционального программирования, борьба с TypeScript, модульное тестирование, написание документации и публикация пакетов NPM и т. д.
Для прочтения статьи вам могут понадобиться следующие основы:
- Основное использование Git
- TypeScript
- ES6
Исходный код проекта
Каталог серий
Зачем использовать функциональное программирование
Поскольку функциональное программирование не изменяет внешние переменные и имеет уникальный вывод для фиксированного ввода, мы можем использовать его независимо от конкретной реализации внутри функции, и очень удобно объединять несколько функций для формирования нужной нам функции. к выражению естественного языка.
Например, мы хотим реализоватьy=f(x)=2*x+1
функция, мы обычно пишем ее так:
function f(x) {
return 2*x + 1;
}
f(1); // 3
А функциональное программирование состоит в том, чтобы разбить их на несколько небольших функций, а затем собрать для использования:
function double(x) {
return 2*x;
}
function plusOne(x) {
return x + 1;
}
plusOne(double(1)); // 3
// 或者还有更好一点的写法,这里暂未实现,
// 这里只是写下他们的调用方法,具体下面的文会讲到
const doubleThenPlusOne = compose(plusOne, double);
doubleThenPlusOne(1);
чистая функция
- неизменный То есть входные параметры и внешние переменные изменить нельзя, и нет побочных эффектов, обеспечивающих «чистоту» функции.
- уникальность
Для каждого фиксированного входного параметра существует уникальный соответствующий выходной результат, что чем-то похоже на математику.
y=f(x)
, когда вводx
без изменений, выводy
не изменится
Вот каштан:
const array = [1, 9, 9, 6];
// slice是纯函数,因为它不会改变原数组,且对固定的输入有唯一的输出
array.slice(1, 2); // [9, 9]
array.slice(1, 2); // [9, 9]
// splice不是纯函数,它即改变原数组,且对固定输入,输出的结果也不同
array.splice(0, 1); // [9 ,9 ,6]
array.splice(0, 1); // [9 ,6]
карри
Каррирование вызывает функцию, передавая ей некоторые аргументы и возвращая функцию для обработки остальных аргументов. Мы реализовали функцию, которая добавляет единицу, но когда нам нужна еще одна функция, которая добавляет две, и переписать код для ее реализации, эффективность очень низкая, поэтому нам нужно каррирование. Давайте представим, возможно ли это?
const plusOne = add(1);
const plusTwo = add(2);
plusOne(1); // 2
plusTwo(2); // 4
Таким образом, мы можем легко получить желаемую функцию, следующееadd
Реализация функции:
function add(a) {
return function(b) {
return a + b;
}
}
Хотя это в основном соответствует нашим текущим потребностям, это все еще кажется неудобным.Если мы хотим добавить три или более чисел, нам, возможно, придется написать это так:
function add(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
Итак, давайте представим себе более удобный способ:
function add(a, b, c) {
return a + b + c;
}
const curryAdd = curry(add);
const plusOne = curryAdd(1);
const plusOneAndTwo = curryAdd(1, 2);
plusOne(2, 3); // 6
plusOneAndTwo(3); // 6
curryAdd(1)(2, 3); // 6
curryAdd(1)(2)(3); // 6
Таким образом, мы можем свободно генерировать функции, требующие различных параметров.curry
Метод реализации (заинтересованные студенты могут сначала подумать, а потом посмотреть):
function curry<Return>(fn: Function): CurryFunction<Return> {
// 记录传进来的函数总共需要多少个参数
let paramsLength: number = fn.length;
function closure(params: any[]): CurryFunction<Return> {
let wrapper: CurryFunction<Return> = function (...newParams: any[]) {
// 将所有的参数取出
let allParams = [...params, ...newParams];
if (allParams.length < paramsLength) {
// 如果参数数量还不够则返回新的函数
return closure(allParams);
} else {
// 否则返回结果
return fn.apply(null, allParams);
}
};
return wrapper;
}
return closure([]);
}
Возможно, некоторые из них не очень просты для понимания, студенты, которые какое-то время не понимают, могут пропустить это и посмотреть ниже~
вотисходный код,а такжеопределение заголовочного файла
Как вариант, роднойbind
функция для реализации каррирования:
const plusOne = add.bind(null, 1);
plusOne(2, 3);
Композиция функций (Compose)
Композиция функций — это объединение нескольких различных функций в новую функцию.
Например:
// 将函数从右往左组合
const doubleThenPlusOne = compose(plusOne, double);
// 1*2 + 1
doubleThenPlusOne(1); // 3
function compose<Return>(...fn: any[]): (...params: any[]) => Return {
return (...params: any[]): Return => {
let i = fn.length - 1;
let result = fn[i].apply(null, params);
while (--i >= 0) {
result = fn[i](result);
}
return result;
};
}
вотисходный код,а такжеопределение заголовочного файла
отложенный вывод
Иногда мир не так прекрасен, и не весь код такой «чистый», как ожидание операций ввода-вывода и операций DOM, потому что эти операции зависят от внешнего мира и влияют на него. В настоящее время нам нужно использовать отложенный вывод, чтобы убедиться, что наша функция «чиста», например, реализованная ниже.random
функция:
function random(min: number = 0, max: number, float: boolean): () => number {
return (): number => {
if (min > max) {
[min, max] = [max, min];
}
if (float || min % 1 || max % 1) {
return min + Math.random() * (max - min);
}
return min + Math.floor(Math.random() * (max - min + 1));
};
}
Для фиксированного ввода он всегда возвращает функцию, которая генерирует случайное число, удовлетворяющее условиям, так что мы можем сохранить наш код «чистым» за счет «прокрастинации», не правда ли, это очень умно! Преимущество этого в том, что он запоминает параметры через механизм закрытия, кэширует их и не нужно повторно передавать одни и те же параметры в следующий раз:
const createRandomNumber = random(1, 100, false);
createRandomNumber();
createRandomNumber(); // 可以多次重复调用产生1到100随机数
Суммировать
В этой главе рассматриваются некоторые из основных концепций функционального программирования и почему его полезно использовать для разработки библиотеки инструментов, поскольку чистые функции «чисты», не зависят от внешнего мира и не оказывают на него никакого влияния, не влияют на него. беспокоиться об этом Влияет на исходный код.
В следующей главе мы поговорим о том, как писать тестовые примеры для наших собственных проектов.