Comments 35
Интересно терминологию перекосило. Было "functions are first-class citizens", а теперь "functions are first-class functions" (проверил, в англоязычных источниках та же фигня).
1) Почему решили писать так:
const add = (x,y) => x+y;
const add = x => y => x + y;
вместо:const add(x,y) => x+y;
const add(x,y) => y => x + y;
Как по мне, второй вариант как-то единообразнее и привычнее. В этом есть какой-то смысл или просто решили так?2) Может я не понимаю, что такое замыкание, но откуда берется Y во втором выражении? Визуально не очевидно, что оно передается в функцию вместе с X.
3) В примере
const impureAddProp = (key, value, object) => {
object[key] = value;//Добавляем свойство объекту
};
const User= {
name: 'Alex'
};
impureAddProp ('isAdmin', true, User);
компилятор будет ругаться?4) В примере
const pureAddProp = (key, value, object) => ({
...object,
[key]: value
});
const User= {
name: 'Alex'
};
const Admin= pureAddProp ('isAdmin', true, User);
разве объект Admin не делит свое состояние с объектом User (имея в виду, что оба они могут быть в реальной жизни намного сложнее)?Или «состояние» — это не значение в рантайме, а код в рантайме? Но тогда не менять код объекта в рантайме это разве фича только ФП?
const add(x,y) => x+y;
const two= 2;
Во втором случае вы тоже предлагаете выбросить =?
const two() => 2;
Кстати насчет привычности — в скале, к примеру, все вообще ровно так же, кроме замены const на val. Так что вопрос привычек — он зависит от предыдущего опыта.
Код:
const add(x,y) => y => x + y;
примет две переменные и вернёт функцию, которая ожидает третью переменную, которая перезапишет одну из первых двух. Поэтому данный вариант немного странен.
Во втором пример Админ не делит состояние с Юзером т.к. свойства Юзера копируются в Админа по значению. В этом и заключается концепция Имутабельности в ФП.
Если вы в серьёз решили изучить ФП в разрезе JS, то мои статьи вам помогут. Пока что достаточно запомнить три описанные концепции. В дальнейшем будут более практические примеры и станет понятнее для чего эти основы(PF, Имутабельность) нужны.
1) Почему решили писать так:
Старая добрая запись никуда не делась
function add(x, y) {
return x + y;
}
ее по-прежнему можно использовать, если штука с const неудобно читается.
2) Может я не понимаю, что такое замыкание, но откуда берется Y во втором выражении? Визуально не очевидно, что оно передается в функцию вместе с X.
То, что здесь происходит, называется каррированием. Вот тут есть отдельная статья с объяснением, что это такое
3) В примере компилятор будет ругаться?
Какой компилятор? В Javascript ругаться точно не будет, но в typescript можно объявить объект неизменяемым и получить ошибку.
const User = { name: 'Alex' } as const; // неизменяемый объект
User.name = 'Boris'; // Error: Cannot assign to 'name' because it is a read-only property.(2540)
// ошибок компиляции нет
const NewUser = { ...User, name: 'Boris' };
Конструкция as const
– это специальный синтаксис Typescript, которого в обычном Javascript нет. Документация вот тут.
4) разве объект Admin не делит свое состояние с объектом User (имея в виду, что оба они могут быть в реальной жизни намного сложнее)?
Все верно, если у вас внутри есть вложенные объекты или массивы, их тоже нужно не забыть склонировать.
const user = {
name: 'Alex',
location: {
country: 'RU'
}
}
// не забываем склонировать location
const newUser = { ...user, location: {...user.location}, name: 'Boris' };
Каждый раз писать такой код может оказаться утомительно, поэтому можно взять утилиту, вроде lodash.cloneDeep.
Старая добрая запись никуда не делась… ее по-прежнему можно использовать, если штука с const неудобно читается.Читается почти удобно, просто синтаксис кажется несколько избыточным…
То, что здесь происходит, называется каррированием. Вот тут есть отдельная статья с объяснением, что это такоеСпасибо, хорошая статья.
Какой компилятор?Извиняюсь, это я из контекста выпал…
Конструкция as const – это специальный синтаксис Typescript, которого в обычном Javascript нет.Приятная конструкция.
Все верно, если у вас внутри есть вложенные объекты или массивы, их тоже нужно не забыть склонировать.Просто в статье было написано «Мы изменяем копию данных, а это всегда безопасно». Но это уже придирки, главное понять что к чему.
Почему решили писать так
Для второго варианта нужно вводить дополнительный синтаксис в язык. Для первого — не нужно, это просто комбинация объявления неизменяемое переменной и стрелочной функции.
откуда берется Y во втором выражении? Визуально не очевидно, что оно передается в функцию вместе с X.
Так оно же передаётся не "вместе", а очень даже раздельно. Функция add принимает x и возвращает анонимную функцию, которая уже принимает y.
Возможно, с лишней парой скобок будет понятнее?
const add = x => (y => x + y);
const add = x => {
const addx = y => x + y;
return addx;
}
1) Если написать код так, как вам хотелось бы — будет просто SyntaxError. Похожим на желаемый синтаксис будет определение именованной функции с помощью function name(arg) {}
.
Это работает, т.к. вы дали функции имя, чтобы ее можно было использовать в дальнейшем. В случае с лямбда-функциями (которые в JS описываются "стрелочным" синтаксисом вроде a => a + 1
) — их не просто так называют анонимными.
При объявлении они не получают имени и поэтому должны быть где-то сохранены, чтобы их можно было использовать в дальнейшем. В данном случае их сохраняют в константу. Поэтому нам нужно объявить константу так же, как обычно. Если в примере const obj = {a: 1}
мы сохранили в константу obj
ссылку на объект { a: 1 }
, то в const func = (x, y) => x + y
мы сохраняем в константу func ссылку на объект функции.
Это единый и привычный синтаксис для данного использования лямбда-функций. Мы также можем объявить лямбда-функцию прямо в аргументах некой функции. Тогда она будет сохранена в замыкании. Пример:
function onClick(elId, callback) {
if (elId == 1) {
callback()
}
}
onClick(1, () => console.log('Вызван коллбек'))
Мы объявляем анонимную функцию, не сохраняя ее в константу. Она будет доступна внутри функции onClick
под именем callback
.
3) У обычного js в целом нет компилятора — это интерпретируемый язык. Данный пример не несет в себе ошибке и НЕ является плохим по определению. Это плохой код только в парадигме функционального программирования, при условии, что есть возможность написать "чистую" функцию.
4) Ответ автора не совсем верен. Как вы правильно упомянули, структура данных User может быть достаточно сложна. В этом случае ...object
скопирует в новый объект только поля и значения первого уровня. Если в нем будут вложенные объекты — они НЕ будут скопированы, и могут изменяться, если какой-нибудь код будет иметь к нему доступ. Пример:
let a = { c: 1 }
let b = { m: 1, a }
let d = { ...b, k: 1 }
a.c = 2
b.m = 2
console.log(d) // { m: 1, a: { c: 2 }, k: 1 }
Как видно, объект a
, вложенный в b
, а потом в d
при изменении повлиял на d
, тогда как при изменении поля m
, которое находится на верхнем уровне объекта b
, объект d
не изменился.
Таким образом, для иммутабельности стоит использовать специальные либы, которые делают "глубокие" копии для объектов, массивов и т.д. В простом варианте это lodash с его cloneDeep или более специализированная Immutable.js
const add = x => y => x + y;
Нет, это не коротко и лаконично, за такое надо руки выравнивать. Особенно когда IDE предлагает add(x) с одним аргументом, и в ответку тебе функция летит. Это очень странно так писать add(1)(2).
const add = (x,y) => x+y
А то что приведено выше, обычно используется для инкапсуляции, например так:
const throttle = action => {
let isRunning = false;
return () => {
if (isRunning) return;
isRunning = true;
setTimeout(() => {
action();
isRunning = false;
}, 10000);
}
};
let throttled = throttle(() => console.log(4));
throttled();throttled();throttled() // callback сработает только один раз
В примере выше инкапсулируется isRunning. В примере со сложением явный оверинжиниринг
const add = x => y => x + y;
Это не более чем пример лямбда функции. Иллюстрация концепции.
const add = x => y => x + y
Такой прием делается не просто для краткости кода, а как минимум для создания:
— композиции функций
— передачи переменных в скоуп функции
к примеру, есть колбэк функция onClick:
<button onClick={}>Delete</button>
и нам необходимо создать универсальную функцию удаления по id, однако функция onClick принимает только один параметр event:
const onClick = event => {
event.preventDefault()
remove(id)
}
для передачи дополнительных переменных в эту функцию без глобального объявления тут не обойтись, как мы знаем глобальные переменные — это зло.
Вот для таких случаях подходит этот прием «функция которая возращает другую функцию»
const deleteOnClick = ({id, remove}) => event => {
event.preventDefault()
remove(id)
}
const DeleteUser = props => (
<button onClick={deleteOnClick(props)}>
Delete
</button>
)
const DeleteProduct = props => (
<button onClick={deleteOnClick(props)}>
Delete
</button>
)
и второй случай:
const add = x => y => x + y
const compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)))
const update = (prop, updater) => obj => ({...obj, [prop]: updater(obj[prop])})
const now = () => new Date
const likePost = compose(
update(`date`, now),
update(`likes`, add(1)),
)
const post = {id: 1, title: 'Learn FP & love it', date: new Date('2019-01-01'), likes: 130}
likePost(post)
В этом примере мы создаем новую функцию likePost на основе уже двух существующих функции.
Например, следующая статья будет о ключевых понятиях ООП: инкапсуляции, абстракции, примесях(и штрихах), интерфейсах и т.дИ будет очередная статья о том, что уже написано тысячи раз.
Лучше бы расширили свой кругозор в ООП и написали о том, с чем большинство веб-разработчиков не знакомы.
Я просто вижу в каких вещах у разработчиков определённого уровня пробелы и решил написать цикл статей, закрывающих эти пробелы. Убить двух зайцев так сказать.
Например, лишних сущностей можно как в ФП, так и в ООП наплодить.
Молотком, гвоздями и лопатой можно как человека бить, так и сарай на даче построить. Это уже на совести человека, как распорядится инструментами.
Например, на текущем проекте у нас действует следующее соглашение:
— в утилях и хелперах только фп
— в колбеках только фп
— в вотчерах компонентов только фп
— в фабриках старые добрые декларации функций
— моделях ес6 классы
На мой взгляд, удобно.
Функциональное программирование с точки зрения EcmaScript. Чистые функции, лямбды, имутабельность