JavaScript 中的纯函数和副作用是什么?
JavaScript 函数简介
函数使我们能够逻辑地放置代码来执行任务。函数Functions
是 JavaScript 编程语言中的“一等公民”。您可以创建、修改函数,将其用作另一个函数的参数,或从函数中返回。您还可以将函数作为值赋给变量。简而言之,如果不使用函数,您几乎无法使用或编写任何有用的 JavaScript 代码。
在本文中,我们将了解Pure Function
它的优点,并探讨Side Effects
其影响。
如果您也喜欢通过视频内容学习,本文也可以作为视频教程在这里获取:🙂
请随时订阅以获取未来的内容
函数可以接受零个或多个输入并产生一个输出。你可以显式地从函数返回输出,或者它只返回一个undefined
。
一个函数明确返回一个值,
// Define the function
function testMe(input) {
// Returns a string value
return `testing ${input}`;
}
// Invoke the function
testMe(123); // returns 'testing 123'
函数没有明确返回值,
// Define the function
function testMe() {
// Do not return anything
}
// Invoke the function
testMe(); // returns undefined
那么,了解了基本用法之后,让我们开始今天的Pure Function
主题。我们还将理解这个概念,Side Effects
以及它对纯函数的影响。
纯函数和副作用示例
作为软件程序员/开发人员,您编写源代码是为了根据输入产生输出。通常,您编写的代码functions
是为了根据输入执行任务并产生输出。我们需要确保这些函数是:
- 可预测:对于相同的输入,它可以产生可预测的输出。
- 可读性:任何将该函数作为独立单元阅读的人都可以完全理解其用途。
- 可重用:可以在源代码的多个地方重用该函数,而无需改变其和调用者的行为。
- 可测试:我们可以将其作为独立单元进行测试。
APure Function
具备上述所有特征。它是一个对相同输入产生相同输出的函数。这意味着传递相同的参数时,它会返回相同的结果。纯函数不应该有任何side effects
改变预期输出的内容。
下面的函数sayGreeting()
是一个纯函数。你能猜一下为什么吗?
function sayGreeting(name) {
return `Hello ${name}`;
}
它是一个纯函数,因为每次传递都会得到一个Hello <name>
输出,而<name>
传递的输入则为一个值。现在,我们来看一下稍有变化的相同函数。
let greeting = "Hello";
function sayGreeting(name) {
return `${greeting} ${name}`;
}
它是纯函数吗?嗯,不是。函数的输出现在取决于一个名为 的外部状态。如果有人将变量greeting
的值更改为 ,会发生什么情况?即使传递相同的输入,函数的输出也会改变。greeting
Hola
sayGreeting()
// When greeting is "Hello"
sayGreeting('Alex'); // Returns, "Hello Alex"
// When greeting is "Hola"
sayGreeting('Alex'); // Returns, "Hola Alex"
因此,在这里我们看到了依赖于外部状态值的副作用,该值可能会在函数没有意识到的情况下发生变化。
一些更典型的副作用案例是,
- 改变输入本身。
- 查询/更新 DOM
- 记录(即使在控制台中)
- 进行 XHR/fetch 调用。
任何与函数最终输出不直接相关的操作都称为Side Effect
。现在让我们看一个impure
函数,在这个函数中,我们改变了输入,并做了一些在纯函数中不应该做的事情。
function findUser(users, item) {
const reversedUsers = users.reverse();
const found = reversedUsers.find((user) => {
return user === item;
});
document.getElementById('user-found').innerText = found;
}
上述函数接受两个参数:一个用户集合(一个数组)和一个要在数组中查找的项目。它通过反转数组末尾来查找该项目。一旦在数组中找到该项目,它就使用 DOM 方法将该值设置为 HTML 元素的文本。
在这里,我们违反了 的两个基本原则pure function
。
- 我们正在改变输入。
- 我们正在查询和操作 DOM
那么,我们可以预见什么样的问题呢?让我们看看。调用者将以findUser()
以下方式调用该函数:
let users = ['Tapas', 'Alex', 'John', 'Maria'];
findUser(users, 'Maria');
在这个阶段,除非调用者读取 findUser() 函数代码,否则调用者可能不知道该函数正在进行 DOM 操作。因此,readability
函数的输出执行的操作与最终结果无关。
此外,我们还修改了输入数组。理想情况下,我们应该克隆输入,然后修改(反转)副本以用于查找操作。现在让我们将其变为一个纯函数。
function findUser(users, item) {
// Create the clone of users array and then reverse
const reversedUsers = [ ...users].reverse();
// Find the element in the cloned array
const found = reversedUsers.find((user) => {
return user === item;
});
// Return the found element
return found;
}
然后,
let users = ['Tapas', 'Alex', 'John', 'Maria'];
let found = findUser(users, 'Maria');
现在该findUser()
函数是一个纯函数。我们消除了改变输入的副作用,并返回预期的输出。因此,该函数具有可读性、可单元测试性、可重用性和可预测性。
纯函数及相关术语
纯函数和副作用是的概念functional programming
。你可能会碰到一些需要友好解释的术语。
- 引用透明性:这意味着我们应该能够用函数的输出值替换函数调用,而不会改变程序的行为。如你所见,只有当函数是 时才有可能
pure function
。
我们来看一个简单的纯函数,
function multipication(x, y) {
return x * y;
}
因此,现在在这个表达式中,我们可以用函数调用的输出值替换它,并保证没有side effect
,
10 + (multiplication(6, 3) ^ 2);
到,
10 + (18 ^ 2);
- 并行代码:纯函数有助于并行执行代码。然而,在 JavaScript 中,代码默认是顺序运行的。
那么,我可以实现所有功能吗Pure Functions
?
是的,从技术上讲,你可以。但是只有纯函数的应用程序可能做不了什么。
您的应用程序可能会产生副作用,例如 HTTP 调用、控制台日志记录、IO 操作等等。请尽可能在需要的地方使用纯函数。并尽可能隔离非纯函数(副作用)。这将大大提高程序的可读性、可调试性和可测试性。
结论
拥抱函数式编程概念(例如纯函数),减少副作用将使你的代码更易于管理和维护。这意味着更少的错误、更快的问题识别和隔离,以及更高的可重用性和可测试性。
如果你想进一步探索这个主题,深入了解函数式编程,请阅读Kyle Simpson 的《Functional-Light JavaScript》。这本书值得一读。
让我们联系吧。我也在这些平台上分享我在 JavaScript、Web 开发和博客方面的学习经验。
文章来源:https://dev.to/atapas/what-are-pure-functions-and-side-effects-in-javascript-429k