Facebook
From Insensitive Crocodile, 9 Years ago, written in Plain Text.
Embed
Download Paste or View Raw
Hits: 720
  1.  
  2. Javascript - programowanie funkcyjne i lambdy
  3. Tuesday, December 16, 2014
  4. Wyobraźmy sobie, że w JavaScript mamy zrobić dwie proste rzeczy:
  5.  
  6. sprawdzić czy wszystkie elementy w tablicy mają konretną wartość
  7. przefiltrowac tablicę
  8.  
  9.  
  10. Standardowe podejście
  11.  
  12. 1)
  13.  
  14. var tab = [ 1, 1, 1];
  15.  
  16. var allTheSame = true;
  17.  
  18. for(var i in tab) {
  19.  
  20. if (tab[i] !== 1) {
  21.  
  22. allTheSame = false;
  23.  
  24. break;
  25.  
  26. }
  27.  
  28. };
  29.  
  30. console.log(allTheSame); // true
  31.  
  32.  
  33.  
  34. 2)
  35.  
  36. var tab = [ 1, 1, 2];
  37.  
  38. var filtered = [];
  39.  
  40. for(var i in tab) {
  41.  
  42. if (tab[i] > 1) {
  43.  
  44. filtered.push(tab[i]);
  45.  
  46. }
  47.  
  48. };
  49.  
  50. console.log(filtered); // [2]
  51.  
  52.  
  53.  
  54.  
  55.  
  56. Niby dobrze. Ale co można poprawić?
  57.  
  58.  
  59.  
  60. Ulepszenie 1 - wbudowane metody
  61.  
  62.  
  63.  
  64. W ECMAScript 5 doszły nowe funkcje na tablicach takie jak every, some, forEach, map, filter, reduce
  65.  
  66. Wspieraję je wszystkie nowe przeglądarki (IE>=9) http://kangax.github.io/compat-table/es5/
  67.  
  68. Teraz nasze przykłady wyglądają następująco:
  69.  
  70. 1)
  71.  
  72. var tab = [ 1, 1, 1];
  73.  
  74. var allTheSame = tab.every(function(el) {
  75.  
  76. return el === 1;
  77.  
  78. });
  79.  
  80. console.log(allTheSame);
  81.  
  82.  
  83.  
  84. 2) filtrowanie
  85.  
  86. var filtered = tab.filter(function(el) {
  87.  
  88. return el > 1;
  89.  
  90. });
  91.  
  92. console.log(filtered); // [2]
  93.  
  94.  
  95.  
  96.  
  97.  
  98. Ulepszenie 2 - różne biblioteki
  99.  
  100.  
  101.  
  102. Jeśli chcemy wspierać starsze przeglądarki lub chcemy mieć wiekśze możliwości możemy wykorzystać darmowe biblioteki.
  103.  
  104. 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ć.
  105.  
  106. Zapis $.grep(tab, function(el, i) {} ) lub $(tab).each(function(i, el) { })
  107.  
  108.  
  109.  
  110. b) Underscore - http://underscorejs.org/
  111.  
  112.  
  113.  
  114. Znana biblioteka. Ogromne możliwości.
  115.  
  116. Przykładowo metody z króych ja korzystałem: findWhere (szukanie po obietach), groupBy, sortBy, contains, isEmpty, pluck. Można robić łańcuchy np.
  117.  
  118. _.chain(stooges)
  119.  
  120. .sortBy(function(stooge){ return stooge.age; })
  121.  
  122. .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
  123.  
  124. .first()
  125.  
  126. .value();
  127.  
  128.  
  129.  
  130. c) Lo-Dash – nieco ulepszony klon underscore
  131.  
  132. https://lodash.com/docs#pluck
  133.  
  134.  
  135.  
  136. d) inne
  137.  
  138. functional.js http://functionaljs.com/
  139.  
  140. np.
  141.  
  142. fjs.every(function (item) {
  143.  
  144. return item % 2 === 0;
  145.  
  146. })([1,2,3]);
  147.  
  148.  
  149.  
  150. Rambda http://ramda.github.io/ramdocs/docs/R.html#gt
  151.  
  152. np.
  153.  
  154. var double = function(x) {
  155.  
  156. return x * 2;
  157.  
  158. };
  159.  
  160. R.map(double, [1, 2, 3]); //=> [2, 4, 6]
  161.  
  162.  
  163.  
  164. linq.js http://linqjs.codeplex.com/ - praktycznie 1:1 z Linq z .net
  165.  
  166. np.
  167.  
  168. Enumerable.Range(1, 10)
  169.  
  170. .Where(function(i) { return i % 3 == 0; })
  171.  
  172. .Select(function(i) { return i * 10; })
  173.  
  174.  
  175.  
  176.  
  177.  
  178. Ulepszenie 3 - funkcje pomocnicze
  179.  
  180.  
  181.  
  182. Żeby kod był jeszcze krótszy i czytelniejszy możemy dodać funkcje pomocnicze do porównywania wartości
  183.  
  184. np.
  185.  
  186.  
  187.  
  188. var eq = function(valueToCompare) {
  189.  
  190. return function(valueInList) {
  191.  
  192. return valueInList === valueToCompare;
  193.  
  194. };
  195.  
  196. };
  197.  
  198.  
  199.  
  200. i teraz zamiast
  201.  
  202. var allTheSame = tab.every(function(el) {
  203.  
  204. return el === 1;
  205.  
  206. });
  207.  
  208.  
  209.  
  210. możemy napisać
  211.  
  212. var allTheSame = tab.every(eq(1));
  213.  
  214.  
  215.  
  216. Oczywiście można dodać dużo więcej takich metod jak gt, notEmpty, and, or itd
  217.  
  218.  
  219.  
  220. Można też skorzystać z biblioteki np. f_underscore.js http://krisjordan.github.io/f_underscore/#
  221.  
  222. np.
  223.  
  224. _.map(stooges, f_.toUpperCase(f_.get('name')));
  225.  
  226.  
  227.  
  228.  
  229.  
  230. Ulepszenie 4 – lambdy
  231.  
  232. wspierają je bilbioteki:
  233.  
  234. a) functional.js http://functionaljs.com/basics/lambda/
  235.  
  236. np. var doubleMap = fjs.map("n => n * 2");
  237.  
  238.  
  239.  
  240. b) linq.js
  241. np.
  242.  
  243. var queryResult2 = Enumerable.From(jsonArray)
  244.  
  245. .Where("$.user.id < 200")
  246.  
  247. .OrderBy("$.user.screen_name")
  248.  
  249. .Select("$.user.screen_name + ':' + $.text")
  250.  
  251. .ToArray();
  252.  
  253.  
  254.  
  255. c) lambda.js - http://www.javascriptoo.com/lambda-js/
  256.  
  257.  
  258.  
  259. Dodaje metodę lamba, która przyjmuje string a zwraca funkcje dzieku temu można tak naprawde użyć jej wszędzie np.
  260.  
  261.  
  262.  
  263. var allTheSame = tab.every(lambda("== 1"));
  264.  
  265.  
  266.  
  267. można sobie oczywiście dać coś krótszego zamiast lambda
  268. np. L tab.every(L("== 1");
  269.  
  270.  
  271.  
  272. Można nawet rozserzyć istniejące metody, żęby akceptowały lambdy
  273.  
  274. ["filter", "some", "every", "map"].forEach(function(fnName) {
  275.  
  276.     var oldF = Array.prototype[fnName];    
  277.  
  278.     Array.prototype[fnName] = function(fun) {
  279.  
  280.         var args = Array.prototype.slice.call(arguments);
  281.  
  282.         if (typeof fun == "string") {        
  283.  
  284.             args[0] = lambda(fun);
  285.  
  286.         }
  287.  
  288.     return oldF.apply(this, args);
  289.  
  290. };
  291.  
  292. });
  293.  
  294.  
  295.  
  296. Jeśli pierwszy argument jest stringiem to zmieniamy go na funkcje za pomocą biblioteki lambda.js i przekazujemy dalej.
  297.  
  298.  
  299.  
  300. i teraz możemy robić tak
  301.  
  302.  
  303.  
  304. [1,2,3].filter(">1").map("x->{key: x + 1}") //[{key: 3}, {key: 4}]
  305.  
  306. Prościej to już chyba się nie da J