Upload
zach-dennis
View
4.659
Download
3
Embed Size (px)
DESCRIPTION
Slides from the "JavaScript Code Organizations, Pattern" talk by Zach Dennis from Mutually Human Software at theGRWebDev meeting on March 28th, 2011
Citation preview
JAVASCRIPT CODE ORGANIZATION, PATTERNS
ZACH DENNIS
MUTUALLY HUMAN SOFTWARE
1Monday, March 28, 2011
INLINE JAVASCRIPTIS NOT SUSTAINABLE
<div class="presentation"> <a onclick="playPresentation()"> Play Presentation </a></div>
2Monday, March 28, 2011
POOR MODULARITY / REUSE
CODE MAINTENANCE PROBLEMS
LACK OF CACHING
DIFFICULTY SCALING TO LARGER APPS
3Monday, March 28, 2011
UNOBTRUSIVE JAVASCRIPTPATHWAY TO THE GREAT VALLEY
4Monday, March 28, 2011
2 TECHNIQUESSEPARATE CONTENT FROM BEHAVIOR,
REGISTER EVENT HANDLERS PROGRAMMATICALLY
5Monday, March 28, 2011
SEPARATE BEHAVIOR FROM CONTENT
<div class="presentation"> <a class="play"> Play Presentation </a></div>
$(“.presentation .play”).click(function(){// ...
});
HTML
JS
6Monday, March 28, 2011
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
THE GREAT VALLEY EFFECT
8Monday, March 28, 2011
A BETTER VANTAGE POINT
SEE PATTERNS
CODE DECOMPOSITION
CODE ORGANIZATION
APPLY PATTERNS
9Monday, March 28, 2011
DRIVES SEMANTICSELECTOR DRIVEN CODE
10Monday, March 28, 2011
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
SELECTOR DRIVEN CODE
IT CAN BE SCANNED AND RE-ORGANIZED MORE
EFFECTIVELY. IT’S EASIER TO VISUALIZE AND MANIPULATE.
12Monday, March 28, 2011
BUT THEN
13Monday, March 28, 2011
$(".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
MONOLITHIC JAVASCRIPTFORMED OF A SINGLE LARGE FILE.
15Monday, March 28, 2011
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
PATTERNS FOR AVOIDING MONOLITHS
TRADITIONAL CLASS-BASED OO
FUNCTIONS, CLOSURES
EVENT-DRIVEN JAVASCRIPT
17Monday, March 28, 2011
TRADITIONAL CLASS-BASED OOAPPLYING TRIED AND TRUE TECHNIQUES WITH JAVASCRIPT
18Monday, March 28, 2011
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
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
var el = $(“.presentation:first”);
var prez = new Presentation(prez);
prez.play();
prez.stop();
JQUERY
USING THE CLASS
21Monday, March 28, 2011
BENEFITS
DATA/BEHAVIOR ENCAPSULATION
CLEAR BOUNDARIES OF RESPONSIBILITIES
API DEFINITION
MODULAR / REUSABLE CODE
22Monday, March 28, 2011
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
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
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
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
FEELS A LITTLE HEAVYFEELS A LITTLE AWKWARD
27Monday, March 28, 2011
SAME EXAMPLE,DIFFERENT PATTERN
28Monday, March 28, 2011
FUNCTIONS, CLOSURESTAKING ADVANTAGE OF JAVASCRIPT
29Monday, March 28, 2011
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
var el = $(“.presentation:first”);
var prez = Presentation(prez);
prez.play();
prez.stop();
JQUERY
ONLY DIFFERENCE, NO “NEW”
31Monday, March 28, 2011
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
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
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
FEELS MORE LIKE HOME
35Monday, March 28, 2011
WHAT IF WE DIDN’T CARE ABOUT A
PUBLIC API?
36Monday, March 28, 2011
(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
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
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
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
ONE STEP FURTHER
41Monday, March 28, 2011
EVENT-DRIVEN JAVASCRIPTDECLARATIVELY READABLE SELECTOR DRIVEN CODE
42Monday, March 28, 2011
6 GUIDELINES
43Monday, March 28, 2011
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
STRUCTURE RELIES ON FUNCTION SCOPE
(function(){
})(); MODULE
$(function(){
}); MODULE JQUERY
EXECUTES IMMEDIATELY
EXECUTES AFTER DOM READY
JS
45Monday, March 28, 2011
DECLARE PAGE CHECKS FIRST
$(function(){ });
JQUERY
if(!$("html.presenter").length) return;PRESENTER.JS
46Monday, March 28, 2011
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
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
QUICKER TO SCAN. KEEPING DECLARATIONS ABOVE
FUNCTION DEFINITIONS CREATES MORE
READABLE CODE.
49Monday, March 28, 2011
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
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
52Monday, March 28, 2011
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
54Monday, March 28, 2011
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