Неосознанно мы подошли к четвертой части серии «Как функциональное программирование на JS».
Первые три портала:
- «XDM, как функциональное программирование на JS? Смотрите, хватит! (один)"
- «XDM, как функциональное программирование на JS? Смотрите, хватит! (два)"
- «XDM, как функциональное программирование на JS? Смотрите, хватит! (три)"
После опыта предыдущих статей Бенгуа считает, что у вас должен быть базовый план функционального программирования в вашем сердце.
В этой статье мы снова визуализируем этот план и поговорим об очень важной детали функционального программирования —"побочный эффект".
- Как три поколения богатых 👍👍👍 Комментарий о красоте жизни🎉🎉🎉
Объяснение побочных эффектов на вики:
Внутри функции существует неявный поток данных, который называется побочным эффектом.
Мы также упоминали ранее:Разработчики предпочитают явный ввод-вывод неявному вводу-выводу.
Итак, мы более подробно рассмотрим разницу между [неявными] и [явными] побочными эффектами!
Какие побочные эффекты?
Начнем с небольшого примера в качестве закуски:
// 片段 1
function foo(x) {
return x * 2;
}
var y = foo( 3 );
// 片段 2
function foo(x) {
y = x * 2;
}
var y;
foo( 3 );
Конечный эффект, достигаемый Фрагментом 1 и Фрагментом 2, одинаков, то есть y = 3 * 2 , но Фрагмент 1 является явным, а Фрагмент 2 - неявным.
Причина в том, что фрагмент 2 ссылается на внешнюю переменную y внутри функции.
Фрагмент 2, когда мы звонимfoo( 3 )
, он не знает, будет ли внешняя переменная y изменена внутри. Его модификация носит неявный характер, т.е. имеет побочные эффекты!
Функции с побочными эффектами менее читабельны, и нам нужно больше читать, чтобы понять программу.
Другой пример:
var x = 1;
foo();
console.log( x );
bar();
console.log( x );
baz();
console.log( x );
Если X ссылается в каждую функцию, можно его изменить.
Если вы решите писать код с (потенциальными) побочными эффектами в одном или нескольких вызовах функций, это означает, что читатели вашего кода должны выполнить вашу программу полностью до строки, шаг за шагом.
еслиfoo()
,bar()
,а такжеbaz()
Эти три функции не имеют (потенциальных) побочных эффектов, значение x видно с первого взгляда!
Должен ли это быть побочным эффектом изменения внешней переменной?
function foo(x) {
return x + y;
}
var y = 3;
foo( 1 );
В этом коде мы не изменяем внешнюю переменную y, но обращение к ней также будет иметь побочные эффекты.
y = 5;
// ..
foo( 1 );
Результат foo( 1 ) отличается дважды, что увеличивает нагрузку на чтение. Поверьте, это самый простой и абстрактный пример, а фактическое влияние будет гораздо больше.
Избежать побочных эффектов?
- const
Возьмем приведенный выше пример: напишите так, результат foo( 1 ), конечно, детерминирован, потому что const используется для фиксации внешней переменной.
const y = 5;
// ..
foo( 1 );
- I/O
Программа без ввода-вывода совершенно бессмысленна, потому что ее работу никак нельзя наблюдать. Полезная программа должна иметь хотя бы один вывод, а также требовать ввода. Вход производит вывод.
Помните фрагмент функции foo(..) 2? Выходной возврат отсутствует, что менее желательно.
// 片段 2
function foo(x) {
y = x * 2;
}
var y;
foo( 3 );
- явная зависимость
У нас часто возникают ошибки данных из-за асинхронности функций; одна функция ссылается на результат обратного вызова другой функции, и нам нужно уделять особое внимание, когда мы делаем такие ссылки.
var users = {};
var userOrders = {};
function fetchUserData(userId) {
ajax( "http://some.api/user/" + userId, function onUserData(userData){
users[userId] = userData;
} );
}
function fetchOrders(userId) {
ajax( "http://some.api/orders/" + userId, function onOrders(orders){
for (let i = 0; i < orders.length; i++) {
// 对每个用户的最新订单保持引用
users[userId].latestOrder = orders[i];
userOrders[orders[i].orderId] = orders[i];
}
} );
}
fetchUserData(..)
должен быть вfetchOrders(..)
выполняется раньше, потому что последний устанавливаетlatestOrder
Требуется обратный вызов первого;
Это нормально писать код, который имеет побочные эффекты/эффекты, но нам нужно быть осторожными и предусмотрительными, чтобы избежать кода с побочными эффектами.
- Использовать идемпотент
Это очень новая, но важная концепция!
С математической точки зрения идемпотент относится к операции, выходные данные которой никогда не меняются, если вы снова и снова вводите эти выходные данные в операцию после первого вызова.
Типичным математическим примером является Math.abs(..) (абсолютное значение). Результат Math.abs(-2) равен 2, что совпадает с результатом Math.abs(Math.abs(Math.abs(Math.abs(-2))))).
Производительность идемпотентности в js:
// 例 1
var x = 42, y = "hello";
String( x ) === String( String( x ) ); // true
Boolean( y ) === Boolean( Boolean( y ) ); // true
// 例 2
function upper(x) {
return x.toUpperCase();
}
function lower(x) {
return x.toLowerCase();
}
var str = "Hello World";
upper( str ) == upper( upper( str ) ); // true
lower( str ) == lower( lower( str ) ); // true
// 例 3
function currency(val) {
var num = parseFloat(
String( val ).replace( /[^\d.-]+/g, "" )
);
var sign = (num < 0) ? "-" : "";
return `${sign}$${Math.abs( num ).toFixed( 2 )}`;
}
currency( -3.1 ); // "-$3.10"
currency( -3.1 ) == currency( currency( -3.1 ) ); // true
Фактически,У нас более широкое понятие идемпотентности в js функциональном программировании, то есть используем только требования:f(x) === f(f(x))
// 幂等的:
obj.count = 2; // 这里的幂等性的概念是每一个幂等运算(比如 obj.count = 2)可以重复多次
person.name = upper( person.name );
// 非幂等的:
obj.count++;
person.lastUpdated = Date.now();
// 幂等的:
var hist = document.getElementById( "orderHistory" );
hist.innerHTML = order.historyText;
// 非幂等的:
var update = document.createTextNode( order.latestUpdate );
hist.appendChild( update );
Мы не всегда определяем данные идемпотентным способом, но если бы мы могли, это определенно уменьшило бы побочные эффекты в непредвиденных ситуациях. Требуется время, чтобы испытать это, поэтому давайте сначала вспомним это.
чистая функция
Вы должны были слышать название чистой функции,Мы называем функции без побочных эффектов чистыми функциями.
пример 1:
function add(x,y) {
return x + y;
}
Оба входа (x и y) и выходы (return ..) являются прямыми, без ссылок на свободные переменные. Нет никакой разницы между многократным вызовом add(3,4) и однократным вызовом. add(..) является идемпотентом в чистом стиле программирования.
Пример 2:
const PI = 3.141592;
function circleArea(radius) {
return PI * radius * radius;
}
CircleArea также является чистой функцией. Хотя он вызывает внешнюю переменную PI, но PI является константой, определенной константой, и обращение к константе не приведет к побочным эффектам;
Пример 3:
function unary(fn) {
return function onlyOneArg(arg){
return fn( arg );
};
}
Унарная тоже чистая функция.
Другой распространенный метод выражения чистоты функции: при одних и тех же входных данных она всегда будет давать один и тот же результат.
Нечистые функции не приветствуются! Потому что нам нужно больше энергии, чтобы судить о его выходе!
Написание чистых функций требует больше терпения, например, метод push(..), с которым мы работаем над массивом, или метод reverse(..) и т. д. Он выглядит безопасным, но на самом деле изменяет сам массив. Нам нужно скопировать переменную для развязки (глубокая копия).
Чистота функции связана с уверенностью. Чем чище функция, тем лучше. Чем усерднее вы работаете над созданием чистой функции, тем увереннее вы будете читать код, который ее использует, что сделает код более читабельным.
На самом деле есть более интересные моменты о чистоте функций:
Подумайте над вопросом, если мы инкапсулируем функцию и внешние переменные в функцию, внешний мир не может напрямую получить доступ к ее внутренней части, поэтому является ли внутренняя функция чистой функцией?
Если дерево падает в лесу и никто вокруг этого не слышит, издает ли оно звук?
резюме этапа
-
Мы неоднократно подчеркиваем:Разработчики предпочитают явный ввод-вывод неявному вводу-выводу.
-
Если есть неявный ввод и вывод, то могут быть и побочные эффекты.
-
Код с побочными эффектами затрудняет понимание нашего кода!
-
Способы решения побочных эффектов: определить константы, очистить ввод-вывод, очистить зависимости, использовать идемпотентность...
-
Использование идемпотентности в js — это новая вещь, с ней нужно знакомиться постепенно.
-
Функция без побочных эффектов — это чистая функция, а чистые функции — это то, что мы стремимся написать!
-
Рефакторинг нечистой функции в чистую предпочтительнее. Однако, если рефакторинг невозможен, попытайтесь инкапсулировать побочные эффекты. (Если в лесу падает дерево и его никто вокруг не слышит, оно издает звук? - неважно, если да, его все равно не слышно)
Вышесказанное касается функционального программирования JS.побочный эффектобъяснение этой детали.
Эта деталь действительно важна!
Я Наггетс Энтони, официальный аккаунт [Наггетс Энтони], вывод раскрывает ввод, техническое понимание жизни!