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

Decoupling JavaScript Vs. The World

Decoupling JavaScript Vs. The World

My presentation from Web Rebels in Norway

* Note: all illustrations of Scott Pilgrim are copyright of Brian O'Malley.

Addy Osmani

June 05, 2012
Tweet

More Decks by Addy Osmani

Other Decks in Programming

Transcript

  1. CONTRIBUTED TO: CREATED: TodoMVC jQuery UI Bootstrap jQuery Plugin Patterns

    Backbone Paginator Feature detection tests Documentation, Writing, Triage Latest Todo application
  2. Coupling is when two (or more) pieces of code must

    know each other in order to function correctly. We consider this bad when this relationship is unnecessary.
  3. Decoupling is when we remove these unnecessary relationships, allowing units

    of code to function independently or free of dependencies they don’t need to be directly tied to.
  4. Applications should be able to function independently or with other

    apps in a container (think widgets) Google Docs (GWT)
  5. Many should be able to work on a codebase without

    being limited by dependencies Another Google project.
  6. Use deferred JavaScript loading to only load chunks/modules as they're

    needed MUCH HARDER IF THEY’RE NOT DECOUPLED Old GMail, Google Docs
  7. GMail & Calendar: Closure AdWords & AdSense: Google Web Toolkit

    Google Docs/Drive : Closure + GWT BTW, in case you were wondering..we use
  8. USE CLOSURE/GWT IF POSSIBLE OR BORROW SOME OF THESE IDEAS

    TO WORK WITH YOUR OWN STACK IF YOU’RE NOT READY. (AND MAYBE TRY OUT SOME OTHER AWESOME ONES TOO)
  9. ALLOW YOU TO CONTROL THE LIFECYCLE OF YOUR WIDGETS? MAXIMIZE

    THE REUSABILITY OF COMPONENTS AND WIDGETS? ALLOW YOU TO EASILY SWITCH LIBRARIES YOU USE WITHOUT MASSIVE CODE RE-WRITES? STILL WORK IF A SPECIFIC MODULE FAILS?
  10. JQUERY: COUPLED, DIRECT CALLS $.when( $.ajax("WebRebels/mail.php") ) .then(function( ajaxArgs ){

    var jqXHR = ajaxArgs[2], data = jqXHR.responseText; // Now has to worry about all of these next... // Display notification and preview modals.show('New Message', data); // Play new message sound audioManager.play('newMessage'); // Update the unread message count messages.increaseMessageCount('unread'); });
  11. JQUERY: DECOUPLED, PUBLISH/SUBSCRIBE // Dialogs $.subscribe('newMessage', function(){ modals.show('New Message', data);

    }); // Sounds $.subscribe('newMessage', function(){ audioManager.play('newMessage'); }); $.when( $.ajax("WebRebels/mail.php") ) .then(function( ajaxArgs ){ var jqXHR = ajaxArgs[2], data = jqXHR.responseText; // Tell the application there is a new message, share the data $.publish('newMessage', data); // Other modules can now just listen out for 'newMessage' });
  12. PUB/SUB WITH RADIO.JS myFunction = function(){ /*...*/ } // create

    topic called newMessage and subscribe myFunction radio('newMessage').subscribe(myFunction); // publish to the topic newMesage radio('newMessage').broadcast(data); // unsubscribe myFunction from the topic newMessage radio('newMessage').unsubscribe(myFunction); // do all of the above in one line via chaining radio('newMessage') .subscribe(myFunction) .broadcast(data) .unsubscribe(myFunction);
  13. PUB/SUB IN NODE WITH FAYE var Faye = require('faye'), server

    = new Faye.NodeAdapter({mount: '/'}); server.listen(8000); // Create a client var client = new Faye.Client('http://localhost:8000/'); // Define subscribers client.subscribe('/messages', function(message) { alert('Got a message: ' + message.text); }); // Publish messages client.publish('/messages', { text: 'Hello Web Rebels!' });
  14. OR WITH BACKBONE EVENTS var myObject = {}; _.extend( myObject,

    Backbone.Events ); // Subscribe myObject.on('eventName', function( msg ) { console.log( 'triggered:' + msg ); }); // Publish myObject.trigger('eventName', 'some event'); // You can even namespace: person.on('change:name change:age', function( msg ) { console.log('name and age changed'); });
  15. LOGICALLY DECOUPLE OBJECTS GENERATING EVENTS FROM THOSE REACTING TO THEM

    NEW MESSAGE ‘HELLO WORLD’ THERE’S A NEW MESSAGE!
  16. $( el ).css() $( el ).animate(..) $( el ).attributes( key,

    value ) ... EXAMPLES YOU USE ALL THE TIME
  17. A SIMPLE FACADE W/ THE MODULE PATTERN var module =

    (function() { var myPrivates = { i:5, get : function() { console.log('current value:' + this.i); }, set : function( val ) { this.i = val; }, run : function() { console.log('running'); } }; return { facade : function( args ) { myPrivates.set(args.val); myPrivates.get(); if ( args.run ) { myPrivates.run(); } } } }()); Badly named, but all of this is private. This facade is all the public interact with. It’s a limited API that takes input like the below and does more work behind the scenes. module.facade({run: true, val:10}); // outputs current value: 10, running
  18. JQUERY’S $(document).ready(..) // We just use this.. $(document).ready(...) or //

    But its a lot more complex than that (this only reflects half of what it does), but the beauty is that we don’t need to worry about the implementation-level details. (function (window) { // Define a local copy of $ var $ = function (callback) { readyBound = false; $.isReady = false; if (typeof callback == 'function') { DOMReadyCallback = callback; } bindReady(); }, // Use the correct document accordingly with window argument (sandbox) document = window.document, readyBound = false, DOMReadyCallback = function () {}, // The ready event handler DOMContentLoaded; // Is the DOM ready to be used? Set to true once it occurs. $.isReady = false; // Handle when the DOM is ready var DOMReady = function () { // Make sure that the DOM is not already loaded if (!$.isReady) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if (!document.body) { setTimeout(DOMReady, 13); return; } // Remember that the DOM is ready
  19. TOWER HANDLES WHAT PLANES CAN TAKE OFF OR LAND ALL

    COMMUNICATION DONE FROM PLANES TO TOWER, NOT PLANE TO PLANE A CENTRALISED CONTROLLER IS KEY TO SUCCESS HERE ANALOGY: AIR TRAFFIC CONTROL
  20. MEDIATOR RESPONSIBLE FOR LETTING COLLEAGUES KNOW OF CHANGES. LIKE PUB/SUB,

    BUT: COLLEAGUES DON’T HAVE TO HOLD REFERENCES TO OTHER COLLEAGUES.
  21. Mediator.js - solid mediator implementation // We just use this..

    $(document).ready(...) or // But its a lot more complex than that (this only reflects half of what it does), but the beauty is that we don’t need to worry about the implementation-level details. (function (window) { // Define a local copy of $ var $ = function (callback) { readyBound = false; $.isReady = false; if (typeof callback == 'function') { DOMReadyCallback = callback; } bindReady(); }, // Use the correct document accordingly with window argument (sandbox) document = window.document, readyBound = false, DOMReadyCallback = function () {}, // The ready event handler DOMContentLoaded; // Is the DOM ready to be used? Set to true once it occurs. $.isReady = false; // Handle when the DOM is ready var DOMReady = function () { // Make sure that the DOM is not already loaded if (!$.isReady) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if (!document.body) { setTimeout(DOMReady, 13); return; } // Remember that the DOM is ready http://thejacklawson.com/ Mediator.js/ http://thejacklawson.com/Mediator.js/
  22. Mediator.js function Subscriber(fn, options, context){ if (!this instanceof Subscriber) {

    return new Subscriber(fn, context, options); }else{ this.id = guidGenerator(); this.fn = fn; this.options = options; this.context = context; this.channel = null; } }; Publish: function(data){ for(var y = 0, x = this._callbacks.length; y < x; y++) { if(!this.stopped){ var callback = this._callbacks[y], l; if(callback.options !== undefined && typeof callback.options.predicate === "function"){ if(callback.options.predicate.apply(callback.context, data)){ callback.fn.apply(callback.context, data); } }else{ callback.fn.apply(callback.context, data); } } l = this._callbacks.length; if(l < x) y--; x = l; }
  23. MODULE PATTERN OBJECT LITERALS AMD ASYNCHRONOUS MODULES - UNTIL WE

    GET ES HARMONY SIMULATED PRIVACY MODULAR OBJECTS
  24. Simple AMD Module // Call define() with an optional ID,

    a dependency array and a factory function allowing us to alias dependencies loaded for usage define(optionalId, ['underscore', 'backbone'], function (_, Backbone) { // Return a defined module return function () {}; });
  25. AMD With Dynamic Dependencies define(function ( require ) { var

    isReady = false, foobar; // note the inline require within our module definition require(['foo', 'bar'], function (foo, bar) { isReady = true; foobar = foo() + bar(); }); // we can still return a module return { isReady: isReady, foobar: foobar }; });
  26. CommonJS: require() and exports // package/lib is a dependency we

    require var lib = require('package/lib'); // some behaviour for our module function foo(){ lib.log('hello world!'); } // export (expose) foo to other modules exports.foo = foo;
  27. ECMAScript 6 Module /* * ES6 Modules (which you can

    use with Google Traceur) * http://traceur-compiler.googlecode.com/git/demo/repl.html */ module Profile { // module code export var firstName = 'Alex'; export var lastName = 'Russell'; export var year = 1973; } module ProfileView { import Profile.{firstName, lastName, year}; function setHeader(element) { element.textContent = firstName + ' ' + lastName; } // rest of module }
  28. NOTIFY US WHEN SOMETHING INTERESTING HAPPENS DON’T TOUCH ANYTHING THEY

    DON’T HAVE TO CAN’T CAUSE THE ENTIRE APP TO STOP WORKING WIDGETS
  29. 1. THE APPLICATION CORE USING THE MEDIATOR PATTERN MANAGES THE

    WIDGET LIFECYCLE: STARTS, STOPS, RESTARTS IF NECESSARY
  30. Thanks RequireJS 2.0! shim config: no need to use patched

    Backbone.js or Underscore.js anymore! require.undef(): Unload specific modules
  31. 1. THE APPLICATION CORE USING THE MEDIATOR PATTERN REACTS TO

    ACTIONS PASSED BACK FROM A SANDBOX (FACADE) - HANDLES LOGIC
  32. 2. THE SANDBOX USING THE FACADE PATTERN AN ABSTRACTION OF

    THE CORE THAT’S AN API FOR COMMON TASKS, USED BY MODULES
  33. 2. THE SANDBOX USING THE FACADE PATTERN INTERFACE FOR ENSURING

    MODULES DON’T DIRECTLY ACCESS THE CORE/LIBRARIES