$30 off During Our Annual Pro Sale. View Details »

JavaScript Funcional

JavaScript Funcional

JavaScript é uma linguagem muito flexível. Você já deve estar acostumado a passar funções como argumentos, ou retorná-las como resultados de chamadas de outras funções. Mas já pensou em fazer das funções a sua principal abstração de código? Nesta palestra vamos ver como usar o JavaScript em uma abordagem funcional pode facilitar o fluxo de desenvolvimento e deixar para traz operações baseadas em loops.

Palestra apresentada na QCon SP 2014

Nando Vieira

April 10, 2014
Tweet

More Decks by Nando Vieira

Other Decks in Programming

Transcript

  1. JS Funcional NANDO VIEIRA // @fnando

  2. Nando Vieira

  3. http://hellobits.com

  4. howto. http://howto.io

  5. http://codeplane.com.br

  6. * * *

  7. O JS não é uma linguagem nova

  8. VS Programação Imperativa Funcional

  9. Programação Funcional

  10. O poder do JS está nas funções

  11. function add(a, b) { return a + b; }

  12. Função é apenas um tipo de objeto

  13. Você pode definir uma função e atribuir a uma variável.

  14. var add = function(a, b) { return a + b;

    };
  15. Você pode executar uma função e retornar uma função.

  16. function adder(a) { return function(b) { return a + b;

    }; }
  17. var plus2 = adder(2); plus2(3); // 5 plus2(5); // 7

  18. Você pode passar funções como argumentos de uma função.

  19. var button = document.querySelector(".button"); ! button.addEventListener("click", function(event){ // Do whatever...

    });
  20. Você pode definir funções dentro de outras funções.

  21. function A() { function B() { console.log("B executed"); } !

    console.log("A executed"); B(); } ! A(); // A executed // B executed
  22. O JavaScript possui muitas características de linguagens funcionais.

  23. E isso permite usar muitos patterns interessantes.

  24. Um pouco sobre Loops

  25. Loops são usados para causar efeito colateral.

  26. var numbers = [1, 2, 3, 4, 5]; ! for

    (var i = 0; i < numbers.length; i++) { console.log(numbers[i]); }
  27. Loops são usados para transformar os dados de uma lista.

  28. var users = [ {name: "John Doe"} , {name: "Mary

    Doe"} ]; var names = []; ! for (var i = 0; i < users.length; i++) { names.push(users[i].name); }
  29. Loops são usados para agregar dados de uma lista.

  30. var sales = [{revenue: 1000}, {revenue: 2000}]; var totalSales =

    0; ! for (var i = 0; i < sales.length; i++) { totalSales += sales[i].revenue; }
  31. Loops são usados para filtrar dados de uma lista.

  32. var users = [{age: 24}, {age: 45}, {age: 13}]; var

    allowedToDrink = []; ! for (var i = 0; i < users.length; i++) { if (users[i].age >= 18) { allowedToDrink.push(users[i]); } }
  33. Loops imperativos focam a lógica do loop.

  34. No JavaScript temos outras alternativas.

  35. Array#forEach foca no efeito colateral.

  36. var numbers = [1, 2, 3, 4, 5]; ! numbers.forEach(function(number){

    console.log(number); });
  37. Array#map foca na transformação.

  38. var users = [ {name: "John Doe"} , {name: "Mary

    Doe"} ]; ! var names = users.map(function(user){ return user.name; });
  39. Transformar arrays é uma tarefa recorrente.

  40. Podemos simplificar com uma função que extrai propriedades.

  41. function attr(name) { return function(object) { return object[name]; }; }

  42. var users = [ {name: "John Doe"} , {name: "Mary

    Doe"} ]; ! var names = users.map(attr("name"));
  43. Array#filter foca no filtro da lista.

  44. var users = [{age: 24}, {age: 45}, {age: 13}]; !

    var allowedToDrink = users.filter(function(user){ return user.age >= 18; });
  45. Array#reduce foca na agregação da lista.

  46. var sales = [{revenue: 1000}, {revenue: 2000}]; var revenues =

    sales.map(attr("revenue")); ! var totalSales = sales.reduce(function(buffer, revenue){ return revenue + buffer; }, 0);
  47. A função de agregação já foi usada antes.

  48. function(buffer, revenue){ return revenue + buffer; }

  49. function add(a, b) { return a + b; }

  50. Juntando todos os pedaços fica mais simples

  51. var sales = [{revenue: 1000}, {revenue: 2000}]; var totalSales =

    sales .map(attr("revenue")) .reduce(add, 0) ;
  52. Exemplo Dada uma lista de pessoas, ordene-a pelo nome e

    renderize cada item como um conteúdo HTML.
  53. Lista Apenas um array de objetos.

  54. var users = [ {name: "John Doe", age: 42} ,

    {name: "Mary Doe", age: 37} , {name: "Eddie Doe", age: 24} ];
  55. Ordenação Utilize a função Array#sort

  56. var ordered = users.sort(function(a, b){ return (a.name < b.name ?

    -1 : 1); });
  57. Renderização Crie uma função que lida com essa abstração.

  58. function UserView(user) { this.user = user; } ! UserView.prototype.render =

    function() { return "<tr><td>" + this.user.name + "</td>" + "<td>" + this.user.age + "</td></tr>" ; };
  59. Agregação Transforme a lista em um resultado agregado.

  60. var html = ""; var view; ! for (var i

    = 0; i < ordered.length; i++) { view = new UserView(ordered[i]); html += view.render(); }
  61. Primeira Solução

  62. function UserView(user) { this.user = user; } ! UserView.prototype.render =

    function() { return "<tr><td>" + this.user.name + "</td>" + "<td>" + this.user.age + "</td></tr>"; }; ! var ordered = users.sort(function(a, b){ return (a.name < b.name ? -1 : 1); }); ! var html = ""; var view; ! for (var i = 0; i < ordered.length; i++) { view = new UserView(ordered[i]); html += view.render(); }
  63. Segunda Solução

  64. function UserView(user) { this.user = user; } ! UserView.prototype.render =

    function() { return "<tr><td>" + this.user.name + "</td>" + "<td>" + this.user.age + "</td></tr>"; }; ! var html = users .sort(function(a, b){ return a.name > b.name ? 1 : -1; }) .map(function(user){ return new UserView(user); }) .map(function(view){ return view.render(); }) .join("\n") ;
  65. Extraia padrões

  66. function asc(attribute) { return function(a, b) { return a[attribute] >

    b[attribute] ? 1 : -1; }; } ! function desc(attribute) { return function(a, b) { return a[attribute] > b[attribute] ? -1 : 1; }; } ! users.sort(asc("name"));
  67. function wrapWith(constructor) { return function(object) { return new constructor(object); };

    } ! users.map(wrapWith(UserView));
  68. function call(funcName) { return function(object) { return object[funcName].call(object); }; }

    ! views.map(call("render"));
  69. users .sort(asc("name")) .map(wrapWith(UserView)) .map(call("render")) .join("\n") ;

  70. Performance importa!

  71. for-loop functional pure functions DOM 0 300,000 600,000 900,000 1,200,000

    37,543 193,477 204,795 1,105,783 Ops/second (maior é melhor) http://fnando.me/10i
  72. Performance importa?

  73. Function Composition pode ajudar! (mas não sempre)

  74. compose(a, b, c);

  75. a(b(c()));

  76. var callRender = call("render"); var wrapView = wrapWith(UserView); var render

    = compose(callRender, wrapView); ! users .sort(asc("name")) .map(render) .join("\n") ;
  77. compose(callRender, wrapView)(user); ! ! callRender(wrapView(user)); é equivalente a

  78. Ferramentas que podem ajudar e inspirar

  79. http://fnando.me/10o

  80. http://fnando.me/10n

  81. http://fnando.me/10r

  82. http://fnando.me/10s

  83. http://fnando.me/10p

  84. http://fnando.me/10q

  85. Aprenda Programação Funcional

  86. http://fnando.me/10j http://fnando.me/10k

  87. http://fnando.me/10l

  88. http://fnando.me/10m

  89. http://fnando.me/10t

  90. Finalizando

  91. Prefira funções puras

  92. Prefira abstrações genéricas

  93. Prefira abstrações menores

  94. Conheça outros paradigmas

  95. Mais uma técnica no seu arsenal.

  96. “Às vezes a implementação mais elegante é uma função. Não

    é um método. Não é uma classe. Não é um framework. É apenas uma função. — John Carmack
  97. Obrigado! @fnando