Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

Cb5d9e9095cd41b636764a85e57ade4b?s=128

Nando Vieira

April 10, 2014
Tweet

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