JavaScript, Spaghetti, and Meatballs (or why modularity matters)

JavaScript, Spaghetti, and Meatballs (or why modularity matters)

My talk on JSconfBR 2013

Cb5d9e9095cd41b636764a85e57ade4b?s=128

Nando Vieira

June 22, 2013
Tweet

Transcript

  1. JavaScript, Spaghetti (or why modularity matters) and Meatballs @fnando http://nandovieira.com

  2. None
  3. THE FIRST REVOLUTION 2004

  4. ASYNCRONOUS JAVASCRIPT AND XML AJAX

  5. THE SECOND REVOLUTION 2006

  6. None
  7. THE THIRD REVOLUTION 2009

  8. None
  9. 29 out of 50* most popular Github repositories are JavaScript-related.

    *bootstrap, node, jquery, html5-boilerplate, impress.js, d3, backbone, chosen, three.js, jQuery-File-Upload, brackets, express, angular.js, Modernizr, meteor, less.js, socket.io, jquery-mobile, underscore, reveal.js, co ee-script, jquery-ui, moment, ember.js, select2, todomvc, backbone-fundamentals, jquery-pjax, pdf.js.
  10. JavaScript is the most important language nowadays.

  11. And JavaScript is almost 20 years old.

  12. So, why do we still suck at it?

  13. "invite_friends" : function() { var resizeLoader = function() { $("#loader").css("width",

    $(".places").width()); $("#loader").css("height", $(".places").height()-18); } resizeLoader(); var resizeTimer; $(window).bind('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(resizeLoader, 50); }); $("a[href=#automatic_invite]").click(function(e){ SomeApp.utils.stopPropagation(e); if(!$(this).parents(".tab_title:first").is(".active")) { $(".tab_title:first") .toggleClass("active"); $(".tab_title:last") .toggleClass("active"); $("#automatic") .toggleClass("hidden"); $("#manual") .toggleClass("hidden"); } }); var suffixes = { "gmail": "@gmail.com", "yahoo": "", "live": "@hotmail.com", "other": ""
  14. The solution is modularity.

  15. Why modular?

  16. Separation of concerns

  17. Easier to expand

  18. Keep your codebase maintainable

  19. Reusability

  20. Testability

  21. Why not, then?

  22. Devs just don’t know how to do it

  23. Writing modular code can be hard

  24. So, how do I do it?

  25. Organization

  26. Treat JavaScript as a real language

  27. Design your code

  28. Use modules

  29. Object literal notation, Module Pattern, AMD, CommonJS, Harmony Modules

  30. Object Literal

  31. var Todo = { tasks: [] , addTask: function(task) {}

    , removeTask : function() {} , count : function() {} };
  32. Module pattern

  33. var Todo = (function(){ var tasks = []; // private

    variable // expose the public API return { addTask: function(task) {} , removeTask: function(task) {} , count: function() {} }; })();
  34. An easy way of emulating method/attribute visibility.

  35. var Todo = (function($, hb){ // all your code here

    })(jQuery, Handlebars);
  36. I tend to use a IIFE & Namespace approach.

  37. Module("MyApp.MyModule", function(MyModule){ MyModule.fn.initialize = function() { // this is the

    initializer }; MyModule.fn.someFunction = function() { // fn is just shortcut for // prototype alas jQuery }; });
  38. None
  39. Modular code will generate lots of les, and that's ok.

  40. Rails asset pipeline concats and mini es all the code.

  41. //= require vendor/jquery //= require_tree ./vendor //= require_tree ./myapp

  42. Dependency order is not a problem.

  43. Single entry point.

  44. // boot.js $(function(){ var html = $("html") , controller =

    html.data("controller") , action = html.data("action") ; MyApp.Application(controller, action); });
  45. ASYNCRONOUS MODULE DEFINITION AMD

  46. Require.js is the de facto library.

  47. Module de nition and script/ le loader.

  48. // sample.js define(function(){ var tasks = []; // private variable

    // expose the public API return { addTask: function(task) {} , removeTask: function(task) {} , count: function() {} }; });
  49. // app.js require(["sample"], function(sample){ // do what you need to

    do with sample });
  50. Explicit dependency is really nice.

  51. On-demand script loading is not a real bene t for

    small-mid apps.
  52. Use r.js before deploying your code.

  53. AMD/Require.js is OK.

  54. But don't say that it allows you to write modular

    code.
  55. You can write modular code with vanilla JavaScript as well.

  56. COMMONJS

  57. Node's module implementation is based on CommonJS.

  58. var tasks = []; // private variable // expose the

    public API module.exports = { addTask: function(task) {} , removeTask: function(task) {} , count: function() {} };
  59. var Todo = require("./todo"); Todo.addTask(task);

  60. I like it more than AMD.

  61. Not that great on the browser.

  62. THE NEW JAVASCRIPT VERSION HARMONY

  63. module Todo { var tasks = []; // private variable

    // expose the public API export default { addTask: function(task) {} , removeTask: function(task) {} , count: function() {} }; }
  64. import Todo from Todo; Todo.addTask(task);

  65. You can import modules from urls.

  66. // load from remote sources module $ from "http://example.org/jquery.js"; //

    Module loader API Loader.load("http://example.org/jquery.js", function($){ // do what you need });
  67. You can be speci c about the importing.

  68. import { a, b, c } from "some/file";

  69. Just a proposal. http://wiki.ecmascript.org/doku.php?id=harmony:modules

  70. Practical example

  71. None
  72. $(".postbox textarea") .on("focus", function(){ $(this).closest(".postbox") .addClass("did-focus") .removeClass("is-contracted") ; }) .on("keyup",

    function(){ var lines = this.value.split(/\r?\n/); var textarea = $(this); if (lines.length >= 5) { textarea.addClass("is-taller"); } else { textarea.removeClass("is-taller"); } }) .on("blur", function(){ if (!this.value) { $(this).closest(".postbox").addClass("is-contracted"); } }) ;
  73. Module("HOWTO.Postbox", function(Postbox){ Postbox.fn.initialize = function(container) { this.container = container; this.textarea

    = container.find(".pb-input"); this.button = container.find(".pb-button"); }; });
  74. Module("HOWTO.Postbox", function(Postbox){ Postbox.fn.initialize = function(container) { // the initialization code

    this.addEventListeners(); }; Postbox.fn.addEventListeners = function() { this.textarea .on("focus", this.onTextareaFocus.bind(this)) .on("keyup", this.onTextareaKeyUp.bind(this)) .on("blur", this.onTextareaBlur.bind(this)) ; }; });
  75. Module("HOWTO.Postbox", function(Postbox){ Postbox.fn.initialize = function(container) {}; Postbox.fn.addEventListeners = function() {};

    Postbox.fn.onTextareaFocus = function(event) { this.container .addClass("did-focus") .removeClass("is-contracted") ; }; });
  76. Module("HOWTO.Postbox", function(Postbox){ Postbox.fn.initialize = function(container) {}; Postbox.fn.addEventListeners = function() {};

    Postbox.fn.onTextareaFocus = function(event) {}; var LINES = 5; Postbox.fn.onTextareaKeyUp = function(event) { var lines = event.target.value.split(/\r?\n/); if (lines.length >= LINES) { this.textarea.addClass("is-taller"); } else { this.textarea.removeClass("is-taller"); } }; });
  77. Module("HOWTO.Postbox", function(Postbox){ Postbox.fn.initialize = function(container) {}; Postbox.fn.addEventListeners = function() {};

    Postbox.fn.onTextareaFocus = function(event) {}; Postbox.fn.onTextareaKeyUp = function(event) {}; Postbox.fn.onTextareaBlur = function(event) { if (!event.target.value) { this.container.addClass("is-contract"); } }; });
  78. describe("HOWTO.Postbox", function() { var container, postbox, textarea; beforeEach(function() { container

    = $("<div/>") .append("<textarea class='pb-input'/>") .append("<button class='pb-button' value='Reply'/>") .appendTo("#sample") ; textarea = container.find("textarea"); postbox = HOWTO.Postbox(container); }); it("sets class when focusing box", function() { textarea.trigger("focus"); expect(container.is(".did-focus")).toBeTruthy(); }); // other specs });
  79. So...

  80. Every complex app must be composed of small components.

  81. Remember that you never know how big it’s going to

    be.
  82. hype Don’t buy into

  83. Modular code can be written without using libraries.

  84. Use what’s better for your work ow.

  85. That’s it!