Heim > Artikel > Web-Frontend > Funktionale JavaScript-Programmierung (1)
Wenn es um funktionale Programmierung geht, ist Ihr erster Eindruck möglicherweise der obskure akademische Code, der voller abstrakter und unbekannter Codes ist etwas zu sein, das nur Computerprofessoren an Universitäten verwenden. Dies mag in einer bestimmten Zeit der Fall gewesen sein, aber in den letzten Jahren hat die funktionale Programmierung mit der Entwicklung der Technologie eine große Rolle in der tatsächlichen Produktion gespielt, und immer mehr Sprachen haben begonnen, Abschlüsse hinzuzufügen , Anonyme Funktionen und andere sehr typische Merkmale der funktionalen Programmierung. In gewissem Maße „assimiliert“ die funktionale Programmierung nach und nach die imperative Programmierung.
JavaScript ist eine typische Multiparadigmen-Programmiersprache. Mit der Popularität von React in den letzten zwei Jahren sind auch RxJS, CycleJS, LodashJS, UnderscoreJS und andere Open-Source-Bibliotheken populär geworden Verwendete Funktionsmerkmale. Lassen Sie uns einige Kenntnisse und Konzepte der funktionalen Programmierung vorstellen.
Wenn Sie sich noch an einige Mathematikkenntnisse in der Mittelschule erinnern, ist das Konzept der Funktion f das für die Eingabe x, eine Ausgabey = f(x). Dies ist die einfachste reine Funktion. Die Definition einer reinen Funktion ist, dass sie für die gleiche Eingabe immer die gleiche Ausgabe ohne erkennbare Nebenwirkungen erhält und nicht vom Zustand der externen Umgebung abhängt.
Lassen Sie uns ein Beispiel geben: In Javascript sind einige Operationen auf Arrays rein, andere nicht:
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); //=> []
In der funktionalen Programmierung wollen wir a reine Funktion wie Slice, keine Funktion wie Splice, die bei jedem Aufruf die Daten durcheinander bringt.
Warum schließt die funktionale Programmierung unreine Funktionen aus? Schauen wir uns ein weiteres Beispiel an:
//不纯的 var min = 18; var checkage = age => age > min; //纯的,这很函数式 var checkage = age => age > 18;
In der unreinen Version hängt das Verhalten der Funktion checkage nicht nur vom Eingabeparameter Alter ab, Es hängt auch von einer externen Variablen min ab. Mit anderen Worten, das Verhalten dieser Funktion muss durch die externe Systemumgebung bestimmt werden. Bei großen Systemen ist diese Abhängigkeit vom externen Zustand der Hauptgrund für die stark zunehmende Systemkomplexität.
Es ist zu erkennen, dass die reine Prüfung die Schlüsselnummer 18 innerhalb der Funktion fest codiert, was eine schlechte Skalierbarkeit aufweist. Wir können es später im Currying sehen wie man dieses Problem mit eleganten Funktionen löst.
Reine Funktionen können nicht nur die Komplexität des Systems effektiv reduzieren, sondern verfügen auch über viele tolle Funktionen, wie z. B. Cachefähigkeit:
import _ from 'lodash'; var sin = _.memorize(x => Math.sin(x)); //第一次计算的时候会稍慢一点 var a = sin(1); //第二次有了缓存,速度极快 var b = sin(1);
Die Definition der Funktion Curry (Curry) ist einfach: Übergeben Sie einen Teil der Parameter an die Funktion, um sie aufzurufen, und lassen Sie sie eine Funktion zurückgeben, die die verbleibenden Parameter verarbeitet.
Zum Beispiel ist Currying für die Additionsfunktion var add = (x, y) => im Allgemeinen nicht sehr nützlich.
//比较容易读懂的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); // =>250Erinnern Sie sich noch an die Checkage-Funktion oben? Wir können es so machen:
Tatsächlich ist Currying eine Methode, eine Funktion „vorab zu laden“, indem weniger Parameter übergeben werden und eine neue Funktion erhalten wird, die sich diese Parameter in gewissem Sinne bereits merkt. sind ein „Caching“ von Parametern und eine sehr effiziente Möglichkeit, Funktionen zu schreiben:
var checkage = min => (age => age > min); var checkage18 = checkage(18); checkage18(20); // =>true
4. Funktionskombination
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"]Lernen Nachdem wir die Verwendung reiner Funktionen und deren Verwendung verstanden haben, lernen wir kann leicht einen solchen „Paket“-Code schreiben:
h(g(f(x)));Obwohl dies auch Funktionscode ist, ist er in gewissem Sinne immer noch „unelegant“. Um das Problem der Funktionsverschachtelung zu lösen, müssen wir „Funktionszusammensetzung“ verwenden:
Die von uns definierte Zusammensetzung ist wie doppelseitiges Klebeband, das zwei beliebige reine Funktionen miteinander kombinieren kann. Natürlich können Sie auch das „dreiseitige Klebeband“, das drei Funktionen vereint, oder sogar „vierseitiges Klebeband“ oder „N-seitiges Klebeband“ erweitern.
//两个函数的组合 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); // =>15Diese flexible Kombination ermöglicht es uns, Funktionscodes wie Bausteine zu kombinieren:
5. Punktefrei
var first = arr => arr[0]; var reverse = arr => arr.reverse(); var last = compose(first, reverse); last([1,2,3,4,5]); // =>5Mit Currying und Grundkenntnissen der Funktionskombination stellen wir die vor Punktfreier Codierungsstil. Wenn Sie vorsichtig sind, werden Sie vielleicht bemerken, dass wir im vorherigen Code immer einige der Methoden, die mit Objekten geliefert werden, gerne in reine Funktionen umwandeln:
var map = (f, arr) => arr.map(f); var toUpperCase = word => word.toUpperCase();
Das hat seinen Grund.
In dieser Funktion verwenden wir str als unsere Zwischenvariable, aber diese Zwischenvariable lässt nicht nur den Code zu Es hat keinen Sinn, etwas mehr als etwas länger zu machen. Lassen Sie uns diesen Code umwandeln:
//这不Piont free var f = str => str.toUpperCase().split(' ');
var toUpperCase = word => word.toUpperCase(); var split = x => (str => str.split(x)); var f = compose(split(' '), toUpperCase); f("abcd efgh"); // =>["ABCD", "EFGH"]
Dieser Stil kann uns helfen, unnötige Benennungen zu reduzieren und den Code einfach und vielseitig zu halten. Um in einigen Funktionen einen punktfreien Stil zu schreiben, müssen natürlich andere Teile des Codes weniger punktfrei sein, und Sie müssen hier Ihre eigenen Entscheidungen treffen.
命令式代码的意思就是,我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。
而声明式就要优雅很多了,我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。
//命令式 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)!