- Javascript - programowanie funkcyjne i lambdy
- Tuesday, December 16, 2014
- Wyobraźmy sobie, że w JavaScript mamy zrobić dwie proste rzeczy:
- sprawdzić czy wszystkie elementy w tablicy mają konretną wartość
- przefiltrowac tablicę
- Standardowe podejście
- 1)
- var tab = [ 1, 1, 1];
- var allTheSame = true;
- for(var i in tab) {
- if (tab[i] !== 1) {
- allTheSame = false;
- break;
- }
- };
- console.log(allTheSame); // true
- 2)
- var tab = [ 1, 1, 2];
- var filtered = [];
- for(var i in tab) {
- if (tab[i] > 1) {
- filtered.push(tab[i]);
- }
- };
- console.log(filtered); // [2]
- Niby dobrze. Ale co można poprawić?
- Ulepszenie 1 - wbudowane metody
- W ECMAScript 5 doszły nowe funkcje na tablicach takie jak every, some, forEach, map, filter, reduce
- Wspieraję je wszystkie nowe przeglądarki (IE>=9) http://kangax.github.io/compat-table/es5/
- Teraz nasze przykłady wyglądają następująco:
- 1)
- var tab = [ 1, 1, 1];
- var allTheSame = tab.every(function(el) {
- return el === 1;
- });
- console.log(allTheSame);
- 2) filtrowanie
- var filtered = tab.filter(function(el) {
- return el > 1;
- });
- console.log(filtered); // [2]
- Ulepszenie 2 - różne biblioteki
- Jeśli chcemy wspierać starsze przeglądarki lub chcemy mieć wiekśze możliwości możemy wykorzystać darmowe biblioteki.
- a) jQuery - ma podstawowe funkcje .grep (filter), .each, .map, .is. Niektóre z nich są ulepszene względem tych z ECMAScript 5 np. z .each można wyjść za pomoća return false a w .map można też filtrować.
- Zapis $.grep(tab, function(el, i) {} ) lub $(tab).each(function(i, el) { })
- b) Underscore - http://underscorejs.org/
- Znana biblioteka. Ogromne możliwości.
- Przykładowo metody z króych ja korzystałem: findWhere (szukanie po obietach), groupBy, sortBy, contains, isEmpty, pluck. Można robić łańcuchy np.
- _.chain(stooges)
- .sortBy(function(stooge){ return stooge.age; })
- .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
- .first()
- .value();
- c) Lo-Dash – nieco ulepszony klon underscore
- https://lodash.com/docs#pluck
- d) inne
- functional.js http://functionaljs.com/
- np.
- fjs.every(function (item) {
- return item % 2 === 0;
- })([1,2,3]);
- Rambda http://ramda.github.io/ramdocs/docs/R.html#gt
- np.
- var double = function(x) {
- return x * 2;
- };
- R.map(double, [1, 2, 3]); //=> [2, 4, 6]
- linq.js http://linqjs.codeplex.com/ - praktycznie 1:1 z Linq z .net
- np.
- Enumerable.Range(1, 10)
- .Where(function(i) { return i % 3 == 0; })
- .Select(function(i) { return i * 10; })
- Ulepszenie 3 - funkcje pomocnicze
- Żeby kod był jeszcze krótszy i czytelniejszy możemy dodać funkcje pomocnicze do porównywania wartości
- np.
- var eq = function(valueToCompare) {
- return function(valueInList) {
- return valueInList === valueToCompare;
- };
- };
- i teraz zamiast
- var allTheSame = tab.every(function(el) {
- return el === 1;
- });
- możemy napisać
- var allTheSame = tab.every(eq(1));
- Oczywiście można dodać dużo więcej takich metod jak gt, notEmpty, and, or itd
- Można też skorzystać z biblioteki np. f_underscore.js http://krisjordan.github.io/f_underscore/#
- np.
- _.map(stooges, f_.toUpperCase(f_.get('name')));
- Ulepszenie 4 – lambdy
- wspierają je bilbioteki:
- a) functional.js http://functionaljs.com/basics/lambda/
- np. var doubleMap = fjs.map("n => n * 2");
- b) linq.js
- np.
- var queryResult2 = Enumerable.From(jsonArray)
- .Where("$.user.id < 200")
- .OrderBy("$.user.screen_name")
- .Select("$.user.screen_name + ':' + $.text")
- .ToArray();
- c) lambda.js - http://www.javascriptoo.com/lambda-js/
- Dodaje metodę lamba, która przyjmuje string a zwraca funkcje dzieku temu można tak naprawde użyć jej wszędzie np.
- var allTheSame = tab.every(lambda("== 1"));
- można sobie oczywiście dać coś krótszego zamiast lambda
- np. L tab.every(L("== 1");
- Można nawet rozserzyć istniejące metody, żęby akceptowały lambdy
- ["filter", "some", "every", "map"].forEach(function(fnName) {
- var oldF = Array.prototype[fnName];
- Array.prototype[fnName] = function(fun) {
- var args = Array.prototype.slice.call(arguments);
- if (typeof fun == "string") {
- args[0] = lambda(fun);
- }
- return oldF.apply(this, args);
- };
- });
- Jeśli pierwszy argument jest stringiem to zmieniamy go na funkcje za pomocą biblioteki lambda.js i przekazujemy dalej.
- i teraz możemy robić tak
- [1,2,3].filter(">1").map("x->{key: x + 1}") //[{key: 3}, {key: 4}]
- Prościej to już chyba się nie da J