maintainable javascript 2011

53
Maintainable JavaScript Nicholas C. Zakas | @slicknet Nicholas C. Zakas | @slicknet flickr.com/photos/jontysewell/4526861658/

Upload: nicholas-zakas

Post on 15-Jan-2015

14.137 views

Category:

Technology


1 download

DESCRIPTION

Writing JavaScript as a hobby and writing JavaScript as a job are two very different things. Learn some common practices for making your JavaScript friendly to a team environment.

TRANSCRIPT

Page 1: Maintainable JavaScript 2011

Maintainable JavaScriptNicholas C. Zakas | @slicknetNicholas C. Zakas | @slicknet

flickr.com/photos/jontysewell/4526861658/

Page 2: Maintainable JavaScript 2011

Who's this guy?

Co-Creator csslint.net

Contributor,Creator of YUI Test

Author Lead Author Contributor Lead Author

5 yearsTech Lead, Yahoo!

Page 3: Maintainable JavaScript 2011

MaintainabilityWhy do we care?

Page 4: Maintainable JavaScript 2011

Most of your time is spent maintaining code

flickr.com/photos/indraw/4857101224/

Page 5: Maintainable JavaScript 2011

Who cares?

Your Employer Your Co-workers,Present and Future

Page 6: Maintainable JavaScript 2011

flickr.com/photos/protestphotos1/4726566233/

We all want to be rock stars "Don't mess with my process, man! It's about the music!"

Page 7: Maintainable JavaScript 2011

At work, you're part of a teamAwesome happens when everyone is on the same page

flickr.com/photos/the_junes/3120810156/

Page 8: Maintainable JavaScript 2011

Maintainable code is

UnderstandableUnderstandable

IntuitiveIntuitive

AdaptableAdaptable

ExtendableExtendable

DebuggableDebuggable

TestableTestable

Page 9: Maintainable JavaScript 2011

Code Conventions

UnderstandableUnderstandable

IntuitiveIntuitive

Page 10: Maintainable JavaScript 2011

flickr.com/photos/29271559@N02/5799773313/

"Programs are meant to be read by humans and only incidentally for computers to execute."

Donald Knuth

Page 11: Maintainable JavaScript 2011

flickr.com/photos/polinasergeeva/3052378826/

4 spaces for indentation

4 spaces for indentation

Tabs for indentationTabs for

indentation

Indentation

Page 12: Maintainable JavaScript 2011

if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type

== 'object') { Y.mix(r[p], s[p]);} else if (ov || !(p in r)) { r[p] = s[p]; } } } }

Page 13: Maintainable JavaScript 2011

if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }

Page 14: Maintainable JavaScript 2011

flickr.com/photos/polinasergeeva/3052378826/

Comments

Page 15: Maintainable JavaScript 2011

/** * Returns a new object containing all of the properties of * all the supplied objects. The properties from later objects * will overwrite those in earlier objects. Passing in a * single object will create a shallow copy of it. For a deep * copy, use clone. * @method merge * @for YUI * @param arguments {Object*} the objects to merge. * @return {object} the new merged object. */Y.merge = function() { var a = arguments, o = {}, i, l = a.length; for (i = 0; i < l; i = i + 1) { Y.mix(o, a[i], true); } return o;}; Every method

Page 16: Maintainable JavaScript 2011

if (mode) { switch (mode) { case 1: // proto to proto return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge); case 2: // object to object and proto to proto Y.mix(r.prototype, s.prototype, ov, wl, 0, merge); break; // pass through case 3: // proto to static return Y.mix(r, s.prototype, ov, wl, 0, merge); case 4: // static to proto return Y.mix(r.prototype, s, ov, wl, 0, merge); default: // object to object is what happens below }}

Difficult-to-understand code

Page 17: Maintainable JavaScript 2011

while (element &&(element = element[axis])){ //NOTE: assignment if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) { return element; }}

Code that might seem to be wrong

Page 18: Maintainable JavaScript 2011

Namingflickr.com/photos/kaatje/243834320/

Page 19: Maintainable JavaScript 2011

Naming• Use logical names for variables and functions

– Don't worry about length• Variable names should be nouns• Function names should begin with a verb (i.e.

getName())– Functions return booleans should begin with

"is", such as isValid()• Avoid useless names such as foo and temp

Page 20: Maintainable JavaScript 2011

if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }

Page 21: Maintainable JavaScript 2011

Loose Coupling

AdaptableAdaptable

ExtendableExtendable

DebuggableDebuggable

Page 22: Maintainable JavaScript 2011

Front End Layers

Base JSBase JSBase JSBase JSBase JSBase JSPresentation

(CSS)Presentation

(CSS)Behavior

(JavaScript)Behavior

(JavaScript)

Data/Structure (HTML)

Data/Structure (HTML)

Page 23: Maintainable JavaScript 2011

Don't cross the streams

Page 24: Maintainable JavaScript 2011

<button onclick="doSomething()">Click Me</button>

Keep JavaScript out of HTML

Page 25: Maintainable JavaScript 2011

var element = document.getElementById("container");element.innerHTML = "<div class=\"popup\"></div>";

Keep HTML out of JavaScript

Page 26: Maintainable JavaScript 2011

.foo { width: expression(document.offsetWidth + "px");}

Keep JavaScript out of CSS

Page 27: Maintainable JavaScript 2011

var element = document.getElementById("container");element.style.color = "red";element.style.cssText = "background:blue;border:1px solid red";

Keep CSS out of JavaScript

Page 28: Maintainable JavaScript 2011

Programming Practices

AdaptableAdaptable

ExtendableExtendable

DebuggableDebuggable

TestableTestable

Page 29: Maintainable JavaScript 2011

//the wrong way!!!function handleClick(event){

var popup = document.getElementById("popup"); popup.style.left = event.clientX + "px"; popup.style.top = event.clientY + "px"; popup.className = "reveal";

}

Event handlers should only handle events

Page 30: Maintainable JavaScript 2011

//better, but still wrongfunction handleClick(event){ showPopup(event);}

function showPopup(event){ var popup = document.getElementById("popup"); popup.style.left = event.clientX + "px"; popup.style.top = event.clientY + "px"; popup.className = "reveal";}

Don't pass the event object around

Page 31: Maintainable JavaScript 2011

//win!!function handleClick(event){ showPopup(event.clientX, event.clientY);}

function showPopup(x, y){ var popup = document.getElementById("popup"); popup.style.left = x + "px"; popup.style.top = y + "px"; popup.className = "reveal";}

Properly separated event handling

Page 32: Maintainable JavaScript 2011

//don't add new methodsArray.prototype.awYeah = function(){ alert("Aw yeah!");};

//don't override methodsYUI.use = function(){ alert("Aw yeah!");};

Don't modify objects you don't ownIf you didn't define the object yourself, you don't own it

Page 33: Maintainable JavaScript 2011

nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/

Page 34: Maintainable JavaScript 2011

function handleClick(event){ showPopup(event.clientX, event.clientY);}

function showPopup(x, y){ var popup = document.getElementById("popup"); popup.style.left = x + "px"; popup.style.top = y + "px"; popup.className = "reveal";}

Avoid global functions and variables

Page 35: Maintainable JavaScript 2011

var Controller = { handleClick: function(event){ this.showPopup(event.clientX, event.clientY); },

showPopup: function (x, y){ var popup = document.getElementById("popup"); popup.style.left = x + "px"; popup.style.top = y + "px"; popup.className = "reveal"; }};

Avoid global functions and variablesCreate a single global (if necessary) and attach everything to it

Page 36: Maintainable JavaScript 2011

var Controller = { addClass: function(element, className){ element.className += " " + className; }};

Throw your own errorsWhen you know a function will fail

Page 37: Maintainable JavaScript 2011

var Controller = { addClass: function(element, className){ if (!element) { throw new Error("addClass: 1st argument missing."); } element.className += " " + className; }};

Throw your own errorsWhen you know a function will fail

Page 38: Maintainable JavaScript 2011

var Controller = { process: function(items){ if (items != null){ items.sort(); items.forEach(function(item){ //do something }); } }};

Avoid null comparisons

Page 39: Maintainable JavaScript 2011

var Controller = { process: function(items){ if (items instanceof Array){ items.sort(); items.forEach(function(item){ //do something }); } }};

Avoid null comparisonsTest for precisely what you want to know if it matters

Page 40: Maintainable JavaScript 2011

Avoid null comparisons• Use instanceof to test for specific object

types– object instanceof MyType

• Use typeof to test for primitive types– typeof value == "string"– BEWARE: typeof null == "object"

Page 41: Maintainable JavaScript 2011

function validate(value) { if (!value) { alert("Invalid value"); location.href = "/errors/invalid.php"; }}

Separate config data

Page 42: Maintainable JavaScript 2011

var config = { urls: { invalid: "/errors/invalid.php" }, strs: { invalidmsg: "Invalid value" }};

function validate(value) { if (!value) { alert(config.strs.invalidmsg); location.href = config.urls.invalid; }}

Separate config data

Page 43: Maintainable JavaScript 2011

Separate Config Data• All URLs needed by the JavaScript• Any strings that are displayed to the user• Any HTML that needs to be created from

JavaScript• Settings (i.e., items per page)• Repeated unique values• Any value that may change in the future

Page 44: Maintainable JavaScript 2011

Build Process

UnderstandableUnderstandable

TestableTestable

Page 45: Maintainable JavaScript 2011

Build Process

BuildBuild

Page 46: Maintainable JavaScript 2011

BuildBuild

ValidateCode

ValidateCode

Concatenate Files

Concatenate Files

GenerateDocumentation

GenerateDocumentation

Add/RemoveDebugging

Add/RemoveDebugging

Minify FilesMinify Files

DeployFiles

DeployFiles

Page 47: Maintainable JavaScript 2011

BuildBuild

DevelopmentDevelopment DeploymentDeploymentTestingTesting

Page 48: Maintainable JavaScript 2011

Recommendations• One object or object definition per file

– Track dependencies• Use a build process to combines files

– Determines correct order– Validates code (JSHint)– Minifies code (YUI Compressor)– Generate documentation (YUI Doc)

Page 49: Maintainable JavaScript 2011

http://www.julienlecomte.net/blog/2007/09/16/

Page 50: Maintainable JavaScript 2011

Recap

Page 51: Maintainable JavaScript 2011

Remember• Code conventions ensure everyone's speaking

the same language• Loose coupling of layers make changes and

debugging easier• Good programming practices allow you to • Code organization and a build process help to

bring sanity to an otherwise crazy process

Page 52: Maintainable JavaScript 2011

Questions?

Page 53: Maintainable JavaScript 2011

Etcetera• My blog: www.nczonline.net• Twitter: @slicknet• These Slides: slideshare.net/nzakas