javascript code organizations, patterns slides - zach dennis

55
JAVASCRIPT CODE ORGANIZATION, PATTERNS ZACH DENNIS MUTUALLY HUMAN SOFTWARE 1 Monday, March 28, 2011

Upload: zach-dennis

Post on 05-Dec-2014

4.655 views

Category:

Technology


3 download

DESCRIPTION

Slides from the "JavaScript Code Organizations, Pattern" talk by Zach Dennis from Mutually Human Software at theGRWebDev meeting on March 28th, 2011

TRANSCRIPT

Page 1: JavaScript Code Organizations, Patterns Slides - Zach Dennis

JAVASCRIPT CODE ORGANIZATION, PATTERNS

ZACH DENNIS

MUTUALLY HUMAN SOFTWARE

1Monday, March 28, 2011

Page 2: JavaScript Code Organizations, Patterns Slides - Zach Dennis

INLINE JAVASCRIPTIS NOT SUSTAINABLE

<div class="presentation"> <a onclick="playPresentation()"> Play Presentation </a></div>

2Monday, March 28, 2011

Page 3: JavaScript Code Organizations, Patterns Slides - Zach Dennis

POOR MODULARITY / REUSE

CODE MAINTENANCE PROBLEMS

LACK OF CACHING

DIFFICULTY SCALING TO LARGER APPS

3Monday, March 28, 2011

Page 4: JavaScript Code Organizations, Patterns Slides - Zach Dennis

UNOBTRUSIVE JAVASCRIPTPATHWAY TO THE GREAT VALLEY

4Monday, March 28, 2011

Page 5: JavaScript Code Organizations, Patterns Slides - Zach Dennis

2 TECHNIQUESSEPARATE CONTENT FROM BEHAVIOR,

REGISTER EVENT HANDLERS PROGRAMMATICALLY

5Monday, March 28, 2011

Page 6: JavaScript Code Organizations, Patterns Slides - Zach Dennis

SEPARATE BEHAVIOR FROM CONTENT

<div class="presentation"> <a class="play"> Play Presentation </a></div>

$(“.presentation .play”).click(function(){// ...

});

HTML

JS

6Monday, March 28, 2011

Page 7: JavaScript Code Organizations, Patterns Slides - Zach Dennis

REGISTER HANDLERS PROGRAMMATICALLY

$(“.presentation .play”).click(function(){// ...

}); JQUERY

$(“.presentation .play”).addEvent(“click”, function(){// ...

});MOOTOOLS

var el = dojo.query(“.presentation .play”);el.connect(“click”, function(){

// ... });

DOJO

7Monday, March 28, 2011

Page 8: JavaScript Code Organizations, Patterns Slides - Zach Dennis

THE GREAT VALLEY EFFECT

8Monday, March 28, 2011

Page 9: JavaScript Code Organizations, Patterns Slides - Zach Dennis

A BETTER VANTAGE POINT

SEE PATTERNS

CODE DECOMPOSITION

CODE ORGANIZATION

APPLY PATTERNS

9Monday, March 28, 2011

Page 10: JavaScript Code Organizations, Patterns Slides - Zach Dennis

DRIVES SEMANTICSELECTOR DRIVEN CODE

10Monday, March 28, 2011

Page 11: JavaScript Code Organizations, Patterns Slides - Zach Dennis

REINFORCE SEMANTIC MARKUP

$(“.presentation .play”).click(function(){// ...

}); JS

<div class="presentation"> <a class="play"> Play Presentation </a></div> HTML

.presentation .play { background-image: url(...); font-size: 1.em;} CSS

11Monday, March 28, 2011

Page 12: JavaScript Code Organizations, Patterns Slides - Zach Dennis

SELECTOR DRIVEN CODE

IT CAN BE SCANNED AND RE-ORGANIZED MORE

EFFECTIVELY. IT’S EASIER TO VISUALIZE AND MANIPULATE.

12Monday, March 28, 2011

Page 13: JavaScript Code Organizations, Patterns Slides - Zach Dennis

BUT THEN

13Monday, March 28, 2011

Page 14: JavaScript Code Organizations, Patterns Slides - Zach Dennis

$(".presentation .play").click(function(){ var presentation = $(this).parents('.presentation:first'); presentation.addClass('playing'); selectSlide(presentation.find(".slides:first"));});

$(".presentation .stop").click(function(){ $(this).parents('.deck:first').addClass('stopping');});

$('.presentation a.destroy').live('ajax:success', function(data){ var deck = $(this).parents('.deck:first'); deck.fadeOut('fast', deck.remove);});

$('#grid .slide a:last').click(function(){ selectSlide($(this).parents(".slide:first")); return false;});

$('img.slide').live("slide:loaded", function(){ resizeSlide($(this));});

// any time the window resizes, resize the main slide$(window).resize(function(){ resizeSlide($(".slide_viewer img.slide"));});

function resizeSlide(img) { var viewer_width = $('.slide_viewer').width(); var viewer_height = $('.slide_viewer').height(); // Use original width and height since the image may be scaled // down to a smaller size, and we want to use the original size to scale // the image rather than the size it is currently scaled to var slide_width = img.data('original_width'); var slide_height = img.data('original_height'); if(slide_width > viewer_width){ ratio = viewer_width / slide_width; $('.slide_viewer img.slide').css({width: viewer_width, height: slide_height * ratio}); }}

page 1 of 22

14Monday, March 28, 2011

Page 15: JavaScript Code Organizations, Patterns Slides - Zach Dennis

MONOLITHIC JAVASCRIPTFORMED OF A SINGLE LARGE FILE.

15Monday, March 28, 2011

Page 16: JavaScript Code Organizations, Patterns Slides - Zach Dennis

CODE MONOLITHS

GREAT FOR DEPLOYMENT

BAD FOR DEVELOPMENT

LOSES CONTEXT, HIERARCHY, SCOPE

VISUALLY HARD TO SCAN

CODE WITH DIFFERENT BEHAVIORS OR REASONS TO EXIST, CO-EXIST

EXCEPT VERY SMALL SITES / APPS

16Monday, March 28, 2011

Page 17: JavaScript Code Organizations, Patterns Slides - Zach Dennis

PATTERNS FOR AVOIDING MONOLITHS

TRADITIONAL CLASS-BASED OO

FUNCTIONS, CLOSURES

EVENT-DRIVEN JAVASCRIPT

17Monday, March 28, 2011

Page 18: JavaScript Code Organizations, Patterns Slides - Zach Dennis

TRADITIONAL CLASS-BASED OOAPPLYING TRIED AND TRUE TECHNIQUES WITH JAVASCRIPT

18Monday, March 28, 2011

Page 19: JavaScript Code Organizations, Patterns Slides - Zach Dennis

function Presentation(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this));};

Presentation.prototype.play = function() { this._element.addClass(“playing”); // ...};

Presentation.prototype.stop = function(hours) { // ...};

JS

19Monday, March 28, 2011

Page 20: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = $.Class.create({

initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... },

}); JQUERY

20Monday, March 28, 2011

Page 21: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var el = $(“.presentation:first”);

var prez = new Presentation(prez);

prez.play();

prez.stop();

JQUERY

USING THE CLASS

21Monday, March 28, 2011

Page 22: JavaScript Code Organizations, Patterns Slides - Zach Dennis

BENEFITS

DATA/BEHAVIOR ENCAPSULATION

CLEAR BOUNDARIES OF RESPONSIBILITIES

API DEFINITION

MODULAR / REUSABLE CODE

22Monday, March 28, 2011

Page 23: JavaScript Code Organizations, Patterns Slides - Zach Dennis

ASSIGN PSEUDO-PRIVATE VARIABLE

var Presentation = $.Class.create({

initialize: function(element) { this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ // ... }, stop: function(){ // ... },});

this._element = element;

this._element.addClass(“playing”);

JQUERY

23Monday, March 28, 2011

Page 24: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = $.Class.create({

initialize: function(element) { this._element = element;

this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... },});

this.play = element.find(“.play”);this.stop = element.find(“.stop”);

FIND AND ASSIGN ELEMENTS WE NEED ACCESS TO

JQUERY

24Monday, March 28, 2011

Page 25: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = $.Class.create({

initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”);

}, play: function(){

this._element.addClass(“playing”); // ... }, stop: function(){ // ...

this.play.bind(“click”, $.proxy(this.play, this));this.stop.bind(“click”, $.proxy(this.stop, this));

JQUERY

REGISTER EVENT HANDLERS

25Monday, March 28, 2011

Page 26: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = $.Class.create({

initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”);

}, play: function(){

._element.addClass(“playing”); // ... }, stop: function(){ // ...

this.play.bind(“click”, );this.stop.bind(“click”, );

JQUERY

KEEP REFERENCE TO THIS

$.proxy(this.play, this);$.proxy(this.stop, this)

this

26Monday, March 28, 2011

Page 27: JavaScript Code Organizations, Patterns Slides - Zach Dennis

FEELS A LITTLE HEAVYFEELS A LITTLE AWKWARD

27Monday, March 28, 2011

Page 28: JavaScript Code Organizations, Patterns Slides - Zach Dennis

SAME EXAMPLE,DIFFERENT PATTERN

28Monday, March 28, 2011

Page 29: JavaScript Code Organizations, Patterns Slides - Zach Dennis

FUNCTIONS, CLOSURESTAKING ADVANTAGE OF JAVASCRIPT

29Monday, March 28, 2011

Page 30: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop);

function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: play, stop: stop }};

JQUERY

FUNCTIONS, SCOPE, CLOSURES

30Monday, March 28, 2011

Page 31: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var el = $(“.presentation:first”);

var prez = Presentation(prez);

prez.play();

prez.stop();

JQUERY

ONLY DIFFERENCE, NO “NEW”

31Monday, March 28, 2011

Page 32: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = function(element){ presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop);

function play(){ // ... } function stop(){ // ... } return { play: play, stop: stop }}; JQUERY

var presentation = element;

ACCESSIBLE PRIVATE VARIABLES

presentation.addClass(“playing”);

32Monday, March 28, 2011

Page 33: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = function(element){ var presentation = element;

function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: play, stop: stop }}; JQUERY

STRAIGHT FORWARD EVENT DELEGATION

presentation.delegate(".play", "click", play);presentation.delegate(".stop", "click", stop);

33Monday, March 28, 2011

Page 34: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop); function play(){ presentation.addClass(“playing”); // ... }; function stop(){ // ... }; return {

};}; JQUERY

API DEFINITION

play: play,stop: stop

34Monday, March 28, 2011

Page 35: JavaScript Code Organizations, Patterns Slides - Zach Dennis

FEELS MORE LIKE HOME

35Monday, March 28, 2011

Page 36: JavaScript Code Organizations, Patterns Slides - Zach Dennis

WHAT IF WE DIDN’T CARE ABOUT A

PUBLIC API?

36Monday, March 28, 2011

Page 37: JavaScript Code Organizations, Patterns Slides - Zach Dennis

(function(){ $(".presentation").delegate(".play", "click", play); $(".presentation").delegate(".stop", "click", stop);

function play(){ $(this).parents(“.presentation:first”).addClass(“playing”); // ... } function stop(){ // ... }})();

SHORTER. SIMPLER. SWEETER.

JQUERY

37Monday, March 28, 2011

Page 38: JavaScript Code Organizations, Patterns Slides - Zach Dennis

CREATED A FUNCTION, EXECUTED IT

REMOVE UNNECESSARY VARS

RELY ON SELECTOR-DRIVEN EVENT HANDLERS

NO NEED TO MANUALLY BIND TO ELEMENTS

WHAT JUST HAPPENED?

38Monday, March 28, 2011

Page 39: JavaScript Code Organizations, Patterns Slides - Zach Dennis

var Presentation = (function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop);

function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: playPresentation, stop: stopPresentation }});

function(){ $(".presentation").delegate(".play", "click", play); $(".presentation").delegate(".stop", "click", stop);

function play(){ $(this).parents(“.presentation:first”).addClass(“playing”); // ... } function stop(){ // ... }}();

var Presentation = $.Class.create({

initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... }});

CLASS-BASED FUNCTION, CLOSURES EVENT-DRIVEN

39Monday, March 28, 2011

Page 40: JavaScript Code Organizations, Patterns Slides - Zach Dennis

FUNCTIONS, CLOSURES RECAP

USE FUNCTIONS AND CLOSURES TO CREATE SCOPE

PRESERVE PRIVATE METHODS AND PROPERTIES WITH VAR STATEMENTS

RETURN PUBLIC METHODS, PROPERTIES (OPTIONAL)

AND WE DON’T POLLUTE GLOBAL NAMESPACE

40Monday, March 28, 2011

Page 41: JavaScript Code Organizations, Patterns Slides - Zach Dennis

ONE STEP FURTHER

41Monday, March 28, 2011

Page 42: JavaScript Code Organizations, Patterns Slides - Zach Dennis

EVENT-DRIVEN JAVASCRIPTDECLARATIVELY READABLE SELECTOR DRIVEN CODE

42Monday, March 28, 2011

Page 43: JavaScript Code Organizations, Patterns Slides - Zach Dennis

6 GUIDELINES

43Monday, March 28, 2011

Page 44: JavaScript Code Organizations, Patterns Slides - Zach Dennis

MEANINGFUL FILE NAMES TO ORGANIZE BEHAVIOR

PRESENTER.JS

VIEWER.JS

SLIDES/

PRESENTER/

REMOTE-CONTROLS.JS

VIEWER/

RESIZING.JS

REMOTE-CONTROLS.JS

GRID.JS

*USE COMBINATOR, COMPRESSOR, MINIFIER FOR DEPLOYMENT

44Monday, March 28, 2011

Page 45: JavaScript Code Organizations, Patterns Slides - Zach Dennis

STRUCTURE RELIES ON FUNCTION SCOPE

(function(){

})(); MODULE

$(function(){

}); MODULE JQUERY

EXECUTES IMMEDIATELY

EXECUTES AFTER DOM READY

JS

45Monday, March 28, 2011

Page 46: JavaScript Code Organizations, Patterns Slides - Zach Dennis

DECLARE PAGE CHECKS FIRST

$(function(){ });

JQUERY

if(!$("html.presenter").length) return;PRESENTER.JS

46Monday, March 28, 2011

Page 47: JavaScript Code Organizations, Patterns Slides - Zach Dennis

DECLARE HANDLERS AND VARS SECOND

$(function(){ if(!$("html.presenter").length) return;

});

var presentation = $(“.presentation”), attendee_count = 0;

presentation.delegate(“.play”, “click”, play);presentation.delegate(“.stop”, “click”, stop);presentation.delegate(“.slide .next”, “click”, nextSlide);presentation.delegate(“.slide .prev”, “click”, prevSlide);

JQUERY

PRESENTER.JS

47Monday, March 28, 2011

Page 48: JavaScript Code Organizations, Patterns Slides - Zach Dennis

DECLARE FUNCTIONS LAST$(function(){ if(!$("html.presenter").length) return;

var presentation = $(“.presentation”), attendee_count = 0;

presentation.delegate(“.play”, “click”, play); presentation.delegate(“.stop”, “click”, stop); presentation.delegate(“.slide .next”, “click”, nextSlide); presentation.delegate(“.slide .prev”, “click”, prevSlide);

function play(){ // ... };

function stop(){ // ...

JQUERY

PRESENTER.JS

48Monday, March 28, 2011

Page 49: JavaScript Code Organizations, Patterns Slides - Zach Dennis

QUICKER TO SCAN. KEEPING DECLARATIONS ABOVE

FUNCTION DEFINITIONS CREATES MORE

READABLE CODE.

49Monday, March 28, 2011

Page 50: JavaScript Code Organizations, Patterns Slides - Zach Dennis

EVENTS DRIVE CROSS-CLOSURE COMMUNICATION

function nextSlide(){ var prez = $(this).parents('.presentation:first'); // ...

current_slide.removeClass('current'); next_slide.addClass('current');

};prez.trigger('slide:changed', { slide: next_slide });

$("body").delegate(".presentation", "slide:changed", transitionToSlide);

THUMBNAIL-CONTROLS.JS

PRESENTER.JS

$("body").delegate(".presentation", "slide:changed", changeSlideOnRemoteViewers);

REMOTE-VIEWER-CONTROLS.JS

_ _ _

50Monday, March 28, 2011

Page 51: JavaScript Code Organizations, Patterns Slides - Zach Dennis

BENEFITS

FUNCTIONS AND CLOSURES ALLOW GROUPING OF COMMON BEHAVIOR AND DATA

CUSTOM EVENTS ARE AWESOME

NO NEED TO HAVE REFERENCES TO EXTERNAL OBJECTS THROUGHOUT OUR APP

LOOSER COUPLING

EASIER TO HOOK IN NEW PARTS OF OUR APP WITH MINIMAL IMPACT TO EXISTING CODE

51Monday, March 28, 2011

Page 52: JavaScript Code Organizations, Patterns Slides - Zach Dennis

52Monday, March 28, 2011

Page 53: JavaScript Code Organizations, Patterns Slides - Zach Dennis

EVENT-DRIVEN HOW WE GOT THERE

MEANINGFUL DIRECTORY AND FILE NAMES

FOLLOW MODULE OR SIMILAR CLOSURE-PATTERN

GENERAL GUIDELINES FOR READABILITY:

DECLARE PAGE CHECKS FIRST

DECLARE HANDLERS AND VARS SECOND

DECLARE FUNCTIONS LAST

USE EVENTS TO DRIVE CROSS-CLOSURE COMMUNICATION

53Monday, March 28, 2011

Page 54: JavaScript Code Organizations, Patterns Slides - Zach Dennis

54Monday, March 28, 2011

Page 55: JavaScript Code Organizations, Patterns Slides - Zach Dennis

IN SUMMARY

INLINE JAVASCRIPT IS NOT A SUSTAINABLE APPROACH.

UNOBTRUSIVE JAVASCRIPT IS A PATH TO “THE GREAT VALLEY”

MONOLITHIC UJS WORKS FINE FOR SMALL APPS, BUT DOESN’T SCALE WELL. FORTUNATELY SELECTOR-DRIVEN JS IS EASIER TO MANIPULATE AND REORGANIZE.

TRADITIONAL CLASS-BASED OO + UJS WORKS WELL, BUT CAN AT TIMES BE A BIT HEAVY

FUNCTION/CLOSURES ARE A LIGHTER WEIGHT APPROACH THAN TRADITIONAL OO. FEELS MORE JAVASCRIPTY.

EVENT-DRIVEN APPROACH WITH EMPHASIS ON FUNCTION/CLOSURES FOR SCOPE AND DECLARATIVE SELECTOR-DRIVEN CODE IS LEANER, SCALABLE AND PROMOTES LOOSE COOUPLING

55Monday, March 28, 2011