Maison > Article > interface Web > Programmation fonctionnelle JavaScript (1)
En matière de programmation fonctionnelle, votre première impression peut être celle de ces codes académiques obscurs, pleins de codes abstraits et inconnus. être quelque chose que seuls les professeurs d'informatique des universités utilisent. Cela était peut-être vrai à une certaine époque, mais ces dernières années, avec le développement de la technologie, la programmation fonctionnelle a joué un rôle énorme dans la production réelle, et de plus en plus de langages ont commencé à ajouter des fermetures , Fonctions anonymes et autres caractéristiques très typiques de la programmation fonctionnelle, dans une certaine mesure, la programmation fonctionnelle « assimile » progressivement la programmation impérative.
JavaScript est un langage de programmation multi-paradigme typique. Avec la popularité de React au cours des deux dernières années, le concept de programmation fonctionnelle est également devenu populaire. RxJS, cycleJS, lodashJS, underscoreJS et d'autres bibliothèques open source. utilisé Caractéristiques fonctionnelles. Introduisons donc quelques connaissances et concepts de programmation fonctionnelle.
Si vous vous souvenez encore de certaines connaissances en mathématiques au collège, la notion de fonction f est celle pour l'entrée x, une Sortiey = f(x). C'est la fonction pure la plus simple. La définition d'une fonction pure est que pour une même entrée, elle obtiendra toujours le même résultat, sans aucun effet secondaire observable, et ne dépend pas de l'état de l'environnement extérieur.
Donnons un exemple. Par exemple, en Javascript, certaines opérations sur les tableaux sont pures, et d'autres ne le sont pas :
var arr = [1,2,3,4,5]; // Array.slice是纯函数,因为它没有副作用,对于固定的输入,输出总是固定的 // 可以,这很函数式 xs.slice(0,3); //=> [1,2,3] xs.slice(0,3); //=> [1,2,3] // Array.splice是不纯的,它有副作用,对于固定的输入,输出不是固定的 // 这不函数式 xs.splice(0,3); //=> [1,2,3] xs.splice(0,3); //=> [4,5] xs.splice(0,3); //=> []
En programmation fonctionnelle, ce que l'on veut, c'est un une fonction pure comme slice, pas une fonction comme splice qui gâche les données à chaque fois qu'elle est appelée.
Pourquoi la programmation fonctionnelle exclut-elle les fonctions impures ? Regardons un autre exemple :
//不纯的 var min = 18; var checkage = age => age > min; //纯的,这很函数式 var checkage = age => age > 18;
Dans la version impure, le comportement de la fonction checkage ne dépend pas seulement du paramètre d'entrée age, Cela dépend également d'une variable externe min En d'autres termes, le comportement de cette fonction doit être déterminé par l'environnement système externe. Pour les grands systèmes, cette dépendance à l’égard d’un état externe est la principale raison de la complexité croissante du système.
On peut remarquer que le pur checkage code en dur le numéro de clé 18 à l'intérieur de la fonction, qui a une mauvaise évolutivité. Nous pouvons le voir dans le currying plus tard. comment résoudre ce problème en utilisant des fonctions élégantes.
Les fonctions pures peuvent non seulement réduire efficacement la complexité du système, mais possèdent également de nombreuses fonctionnalités intéressantes, telles que la mise en cache :
import _ from 'lodash'; var sin = _.memorize(x => Math.sin(x)); //第一次计算的时候会稍慢一点 var a = sin(1); //第二次有了缓存,速度极快 var b = sin(1);
La définition de la fonction curry (curry) est simple : transmettre une partie des paramètres à la fonction pour l'appeler, et la laisser renvoyer une fonction pour gérer le reste des paramètres.
Par exemple, pour la fonction d'addition var add = (x, y) => x y , on peut la curry comme ceci :
//比较容易读懂的ES5写法 var add = function(x){ return function(y){ return x + y } } //ES6写法,也是比较正统的函数式写法 var add = x => (y => x + y); //试试看 var add2 = add(2); var add200 = add(200); add2(2); // =>4 add200(50); // =>250
Pour une fonction extrêmement simple comme l'addition , curry ne sert pas à grand chose.
Vous souvenez-vous encore de la fonction de contrôle ci-dessus ? Nous pouvons le curry comme ceci :
var checkage = min => (age => age > min); var checkage18 = checkage(18); checkage18(20); // =>true
En fait, le curry est une méthode de "préchargement" d'une fonction en passant moins de paramètres et en en obtenant une nouvelle qui mémorise déjà ces paramètres. sont une "mise en cache" de paramètres et un moyen très efficace d'écrire des fonctions :
import { curry } from 'lodash'; //首先柯里化两个纯函数 var match = curry((reg, str) => str.match(reg)); var filter = curry((f, arr) => arr.filter(f)); //判断字符串里有没有空格 var haveSpace = match(/\s+/g); haveSpace("ffffffff"); //=>null haveSpace("a b"); //=>[" "] filter(haveSpace, ["abcdefg", "Hello World"]); //=>["Hello world"]
Apprendre Après avoir compris l'utilisation des fonctions pures et comment les curry, nous peut facilement écrire un tel code "package":
h(g(f(x)));
Bien que ce soit aussi du code fonctionnel, mais il est toujours "inelégant" dans un sens. Afin de résoudre le problème de l'imbrication des fonctions, nous devons utiliser la « composition de fonctions » :
//两个函数的组合 var compose = function(f, g) { return function(x) { return f(g(x)); }; }; //或者 var compose = (f, g) => (x => f(g(x))); var add1 = x => x + 1; var mul5 = x => x * 5; compose(mul5, add1)(2); // =>15
La composition que nous définissons est comme du ruban adhésif double face, qui peut combiner deux fonctions pures ensemble. Bien entendu, vous pouvez également étendre le « ruban adhésif trois faces » qui combine trois fonctions, ou encore le « ruban adhésif quatre faces » ou le « ruban adhésif N-face ».
Cette combinaison flexible nous permet de combiner des codes fonctionnels comme des blocs de construction :
var first = arr => arr[0]; var reverse = arr => arr.reverse(); var last = compose(first, reverse); last([1,2,3,4,5]); // =>5
Avec le curry et une connaissance de base de la combinaison de fonctions, introduisons le Style de codage sans points.
Si vous êtes prudent, vous remarquerez peut-être que dans le code précédent, nous aimons toujours convertir certaines des méthodes fournies avec les objets en fonctions pures :
var map = (f, arr) => arr.map(f); var toUpperCase = word => word.toUpperCase();
Il y a une raison à cela.
Le mode Point Free n'a actuellement pas de traduction chinoise. Si vous êtes intéressé, vous pouvez lire l'explication en anglais ici :
http://www.php.cn/
<.>L'explication chinoise est probablement de ne pas nommer de variables intermédiaires éphémères, telles que ://这不Piont free var f = str => str.toUpperCase().split(' ');Dans cette fonction, nous utilisons str comme variable intermédiaire, mais cette variable intermédiaire ne laisse pas seulement le code Il ne sert à rien d’obtenir plus qu’un peu plus de temps. Transformons ce code :
var toUpperCase = word => word.toUpperCase(); var split = x => (str => str.split(x)); var f = compose(split(' '), toUpperCase); f("abcd efgh"); // =>["ABCD", "EFGH"]
命令式代码的意思就是,我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。
而声明式就要优雅很多了,我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。
//命令式 var CEOs = []; for(var i = 0; i < companies.length; i++){ CEOs.push(companies[i].CEO) } //声明式 var CEOs = companies.map(c => c.CEO);
命令式的写法要先实例化一个数组,然后再对 companies 数组进行for循环遍历,手动命名、判断、增加计数器,就好像你开了一辆零件全部暴露在外的汽车一样,虽然很机械朋克风,但这并不是优雅的程序员应该做的。
声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了。
函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。
相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些不干净的副作用。在复杂的系统中,这对于程序员的心智来说是极大的负担。
任何代码都是要有实际用处才有意义,对于JS来说也是如此。然而现实的编程世界显然不如范例中的函数式世界那么美好,实际应用中的JS是要接触到ajax、DOM操作,NodeJS环境中读写文件、网络操作这些对于外部环境强依赖,有明显副作用的“很脏”的工作。
这对于函数式编程来说也是很大的挑战,所以我们也需要更强大的技术去解决这些“脏问题”。我会在下一篇文章中介绍函数式编程的更加高阶一些的知识,例如Functor、Monad等等概念。
以上就是JavaScript函数式编程(一)的内容,更多相关内容请关注PHP中文网(www.php.cn)!