145
JavaScript “Best Practices” Christian Heilmann | http://wait-till-i.com | http://scriptingenabled.org Bangalore, India, Yahoo internal training, February 2009

Javascript Best Practices

Embed Size (px)

DESCRIPTION

A presentation about tricks and best practice ideas of how to use JavaScript given in our office in India as part of an internal training.

Citation preview

Page 1: Javascript Best Practices

JavaScript

“Best Practices”

Christian Heilmann | http://wait-till-i.com | http://scriptingenabled.org

Bangalore, India, Yahoo internal training, February 2009

Page 2: Javascript Best Practices

“Best Practice” presentations are hard to do.

Page 3: Javascript Best Practices

Trying to tell developers to change their ways and follow your example is a tough call.

Page 4: Javascript Best Practices

I also consider it a flawed process.

Page 5: Javascript Best Practices

I am bored of discussions of syntax details.

Page 6: Javascript Best Practices

This is not about forcing you to believe what I

believe.

Page 7: Javascript Best Practices

This is about telling you what worked for me and might as

well work for you.

Page 8: Javascript Best Practices

I’ve collected a lot of ideas and tips over time how to be

more effective.

Page 9: Javascript Best Practices

Make it understandable

Avoid globals

Stick to a strict coding style

Comment as much as needed but not more

Avoid mixing with other technologies

Use shortcut notations

Modularize

Enhance progressively

Allow for configuration and translation

Avoid heavy nesting

Optimize loops

Keep DOM access to a minimum

Don't yield to browser whims

Don't trust any data

Add functionality with JavaScript, not content

Build on the shoulders of giants

Development code is not live code

Page 10: Javascript Best Practices

Make it understandable!

Page 11: Javascript Best Practices

Choose easy to understand and short names for variables

and functions.

Page 12: Javascript Best Practices

Bad variable names:x1 fe2 xbqne

Page 13: Javascript Best Practices

Also bad variable names:incrementorForMainLoopWhichSpansFromTenToTwenty

createNewMemberIfAgeOverTwentyOneAndMoonIsFull

Page 14: Javascript Best Practices

Avoid describing a value with your variable or function

name.

Page 15: Javascript Best Practices

For example isOverEighteen()

might not make sense in some countries,

isLegalAge() however works everywhere.

Page 16: Javascript Best Practices

Think of your code as a story – if readers get stuck because

of an unpronounceable character in your story that isn’t part of the main story line, give it another, easier

name.

Page 17: Javascript Best Practices

Avoid globals

Page 18: Javascript Best Practices

Global variables are a terribly bad idea.

Page 19: Javascript Best Practices

You run the danger of your code being overwritten by

any other JavaScript added to the page after yours.

Page 20: Javascript Best Practices

The workaround is to use closures and the module

pattern.

Page 21: Javascript Best Practices

var current = null;var labels = [ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’];function init(){};function show(){};function hide(){};

Page 22: Javascript Best Practices

var current = null;var labels = [ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’];function init(){};function show(){ current = 1;};function hide(){ show();};

Everything is global and can be accessed

Page 23: Javascript Best Practices

Problem: access is not contained, anything in the

page can overwrite what you do.

Page 24: Javascript Best Practices

demo = { current:null, labels:[ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’ ], init:function(){ }, show:function(){ demo.current = 1; }, hide:function(){ demo.show(); }}

Object Literal:Everything is

contained but can be accessed via the

object name.

Page 25: Javascript Best Practices

Problem: Repetition of module name leads to huge

code and is annoying.

Page 26: Javascript Best Practices

(function(){ var current = null; var labels = [ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’ ]; function init(){ }; function show(){ current = 1; }; function hide(){ show(); };})();

Anonymous Module:Nothing is global.

Page 27: Javascript Best Practices

Problem: No access from the outside at all (callbacks,

event handlers)

Page 28: Javascript Best Practices

module = function(){ var labels = [ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’ ]; return { current:null, init:function(){ }, show:function(){ module.current = 1; }, hide:function(){ module.show(); } }}();

Module Pattern:You need to specify what is global and

what isn’t – switching syntax in between.

Page 29: Javascript Best Practices

Problem: Repetition of module name, different

syntax for inner functions.

Page 30: Javascript Best Practices

module = function(){ var current = null; var labels = [ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’ ]; function init(){ }; function show(){ current = 1; }; function hide(){ show(); }; return{init:init,show:show,current:current}}();

Revealing Module Pattern:

Keep consistent syntax and mix and

match what to make global.

Page 31: Javascript Best Practices

module = function(){ var current = null; var labels = [ ‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’ ]; function init(){ }; function show(){ current = 1; }; function hide(){ show(); }; return{init:init,show:show,current:current}}();module.init();

Revealing Module Pattern:

Keep consistent syntax and mix and

match what to make global.

Page 32: Javascript Best Practices

Stick to a strict coding style

Page 33: Javascript Best Practices

Browsers are very forgiving JavaScript parsers.

Page 34: Javascript Best Practices

However, lax coding style will hurt you when you shift to

another environment or hand over to another developer.

Page 35: Javascript Best Practices

Valid code is good code.

Page 36: Javascript Best Practices

Valid code is secure code.

Page 37: Javascript Best Practices

Validate your code:

http://www.jslint.com/

Page 39: Javascript Best Practices

Comment as much as needed but no more

Page 40: Javascript Best Practices

Comments are messages from developer to developer.

Page 41: Javascript Best Practices

“Good code explains itself”

is an arrogant myth.

Page 42: Javascript Best Practices

Comment what you consider needed – but don’t tell others

your life story.

Page 43: Javascript Best Practices

Avoid using the line comment though. It is much safer to

use /* */ as that doesn’t cause errors when the line

break is removed.

Page 44: Javascript Best Practices

If you debug using comments, there is a nice

little trick:

Page 45: Javascript Best Practices

module = function(){ var current = null; function init(){ };/* function show(){ current = 1; }; function hide(){ show(); };*/ return{init:init,show:show,current:current}}();

Page 46: Javascript Best Practices

module = function(){ var current = null; function init(){ };/* function show(){ current = 1; }; function hide(){ show(); };// */ return{init:init,show:show,current:current}}();

Page 47: Javascript Best Practices

module = function(){ var current = null; function init(){ };//* function show(){ current = 1; }; function hide(){ show(); };// */ return{init:init,show:show,current:current}}();

Page 48: Javascript Best Practices

Comments can be used to write documentation – just

check the YUI doc:

http://yuiblog.com/blog/2008/12/08/yuidoc/

Page 49: Javascript Best Practices

However, comments should never go out to the end user in plain HTML or JavaScript.

Page 50: Javascript Best Practices

Back to that later :)

Page 51: Javascript Best Practices

Avoid mixing with other technologies

Page 52: Javascript Best Practices

JavaScript is good for calculation, conversion,

access to outside sources (Ajax) and to define the

behaviour of an interface (event handling).

Page 53: Javascript Best Practices

Anything else should be kept to the technology we have to

do that job.

Page 54: Javascript Best Practices

For example:

Put a red border around all fields with a class of “mandatory” when they are empty.

Page 55: Javascript Best Practices

var f = document.getElementById('mainform');var inputs = f.getElementsByTagName('input');for(var i=0,j=inputs.length;i<j;i++){ if(inputs[i].className === 'mandatory' && inputs[i].value === ''){ inputs[i].style.borderColor = '#f00'; inputs[i].style.borderStyle = 'solid'; inputs[i].style.borderWidth = '1px'; }}

Page 56: Javascript Best Practices

Two month down the line:

All styles have to comply with the new company style guide, no borders are allowed and errors should be shown by an alert icon next to the element.

Page 57: Javascript Best Practices

People shouldn’t have to change your JavaScript code to change the look and feel.

Page 58: Javascript Best Practices

var f = document.getElementById('mainform');var inputs = f.getElementsByTagName('input');for(var i=0,j=inputs.length;i<j;i++){ if(inputs[i].className === 'mandatory' && inputs[i].value === ''){ inputs[i].className+=’ error’; }}

Page 59: Javascript Best Practices

Using classes you keep the look and feel to the CSS

designer.

Page 60: Javascript Best Practices

Using CSS inheritance you can also avoid having to loop

over a lot of elements.

Page 61: Javascript Best Practices

Use shortcut notations

Page 62: Javascript Best Practices

Shortcut notations keep your code snappy and easier to

read once you got used to it.

Page 63: Javascript Best Practices

var cow = new Object();cow.colour = ‘white and black’;cow.breed = ‘Holstein’;cow.legs = 4;cow.front = ‘moo’;cow.bottom = ‘milk’;

var cow = { colour:‘white and black’, breed:‘Holstein’, legs:4, front:‘moo’, bottom = ‘milk’};

is the same as

Page 64: Javascript Best Practices

is the same as

var lunch = new Array();lunch[0]=’Dosa’;lunch[1]=’Roti’;lunch[2]=’Rice’;lunch[3]=’what the heck is this?’;

var lunch = [ ‘Dosa’, ‘Roti’, ‘Rice’, ‘what the heck is this?’];

Page 65: Javascript Best Practices

is the same as

if(v){ var x = v;} else { var x = 10;}

var x = v || 10;

Page 66: Javascript Best Practices

is the same as

var direction;if(x > 100){ direction = 1;} else { direction = -1;}

var direction = (x > 100) ? 1 : -1;

/* Avoid nesting these! */

Page 67: Javascript Best Practices

Modularize

Page 68: Javascript Best Practices

Keep your code modularized and specialized.

Page 69: Javascript Best Practices

It is very tempting and easy to write one function that

does everything.

Page 70: Javascript Best Practices

As you extend the functionality you will

however find that you do the same things in several

functions.

Page 71: Javascript Best Practices

To prevent that, make sure to write smaller, generic helper

functions that fulfill one specific task rather than

catch-all methods.

Page 72: Javascript Best Practices

At a later stage you can also expose these when using the revealing module pattern to create an API to extend the

main functionality.

Page 73: Javascript Best Practices

Good code should be easy to build upon without re-writing

the core.

Page 74: Javascript Best Practices

Enhance progressively

Page 75: Javascript Best Practices

There are things that work on the web.

Page 76: Javascript Best Practices

Use these rather than creating a lot of JavaScript

dependent code.

Page 77: Javascript Best Practices

DOM generation is slow and expensive.

Page 78: Javascript Best Practices

Elements that are dependent on JavaScript but are

available when JavaScript is turned off are a broken

promise to our users.

Page 79: Javascript Best Practices

Example: TV tabs.

Page 80: Javascript Best Practices

Tab Interface

Page 81: Javascript Best Practices

Tab Interface

Page 82: Javascript Best Practices

Allow for configuration and translation.

Page 83: Javascript Best Practices

Everything that is likely to change in your code should not be scattered throughout

the code.

Page 84: Javascript Best Practices

This includes labels, CSS classes, IDs and presets.

Page 85: Javascript Best Practices

By putting these into a configuration object and

making this one public we make maintenance very easy and allow for customization.

Page 86: Javascript Best Practices

carousel = function(){ var config = { CSS:{ classes:{ current:’current’, scrollContainer:’scroll’ }, IDs:{ maincontainer:’carousel’ } } labels:{ previous:’back’, next:’next’, auto:’play’ } settings:{ amount:5,

Page 87: Javascript Best Practices

skin:’blue’, autoplay:false } }; function init(){ }; function scroll(){ }; function highlight(){ }; return {config:config,init:init}}();

Page 88: Javascript Best Practices

Avoid heavy nesting

Page 89: Javascript Best Practices

Code gets unreadable after a certain level of nesting –

when is up to your personal preference and pain

threshold.

Page 90: Javascript Best Practices

A really bad idea is to nest loops inside loops as that also means taking care of several iterator variables (i,j,k,l,m...).

Page 91: Javascript Best Practices

You can avoid heavy nesting and loops inside loops with specialized tool methods.

Page 92: Javascript Best Practices

function renderProfiles(o){ var out = document.getElementById(‘profiles’); for(var i=0;i<o.members.length;i++){ var ul = document.createElement(‘ul’); var li = document.createElement(‘li’); li.appendChild(document.createTextNode(o.members[i].name)); var nestedul = document.createElement(‘ul’); for(var j=0;j<o.members[i].data.length;j++){ var datali = document.createElement(‘li’); datali.appendChild( document.createTextNode( o.members[i].data[j].label + ‘ ‘ + o.members[i].data[j].value ) ); nestedul.appendChild(datali); } li.appendChild(nestedul); } out.appendChild(ul);}

Page 93: Javascript Best Practices

function renderProfiles(o){ var out = document.getElementById(‘profiles’); for(var i=0;i<o.members.length;i++){ var ul = document.createElement(‘ul’); var li = document.createElement(‘li’); li.appendChild(document.createTextNode(data.members[i].name)); li.appendChild(addMemberData(o.members[i])); } out.appendChild(ul);}function addMemberData(member){ var ul = document.createElement(‘ul’); for(var i=0;i<member.data.length;i++){ var li = document.createElement(‘li’); li.appendChild( document.createTextNode( member.data[i].label + ‘ ‘ + member.data[i].value ) ); } ul.appendChild(li); return ul;}

Page 94: Javascript Best Practices

Think of bad editors and small screens.

Page 95: Javascript Best Practices

Optimize loops

Page 96: Javascript Best Practices

Loops can get terribly slow in JavaScript.

Page 97: Javascript Best Practices

Most of the time it is because you’re doing things in them

that don’t make sense.

Page 98: Javascript Best Practices

var names = ['George','Ringo','Paul','John'];for(var i=0;i<names.length;i++){ doSomeThingWith(names[i]);}

Page 99: Javascript Best Practices

This means that every time the loop runs, JavaScript

needs to read the length of the array.

Page 100: Javascript Best Practices

You can avoid that by storing the length value in a different

variable:

Page 101: Javascript Best Practices

var names = ['George','Ringo','Paul','John'];var all = names.length;for(var i=0;i<all;i++){ doSomeThingWith(names[i]);}

Page 102: Javascript Best Practices

An even shorter way of achieving this is to create a second variable in the pre-

loop condition.

Page 103: Javascript Best Practices

var names = ['George','Ringo','Paul','John'];for(var i=0,j=names.length;i<j;i++){ doSomeThingWith(names[i]);}

Page 104: Javascript Best Practices

Keep computation-heavy code outside of loops.

Page 105: Javascript Best Practices

This includes regular expressions but first and

foremost DOM manipulation.

Page 106: Javascript Best Practices

You can create the DOM nodes in the loop but avoid

inserting them to the document.

Page 107: Javascript Best Practices

Keep DOM access to a minimum

Page 108: Javascript Best Practices

If you can avoid it, don’t access the DOM.

Page 109: Javascript Best Practices

The reason is that it is slow and there are all kind of

browser issues with constant access to and changes in the

DOM.

Page 110: Javascript Best Practices

Write or use a helper method that batch-converts a dataset

to HTML.

Page 111: Javascript Best Practices

Seed the dataset with as much as you can and then

call the method to render all out in one go.

Page 112: Javascript Best Practices

Don't yield to browser whims!

Page 113: Javascript Best Practices

What works in browsers today might not tomorrow.

Page 114: Javascript Best Practices

Instead of relying on flaky browser behaviour and

hoping it works across the board...

Page 115: Javascript Best Practices

...avoid hacking around and analyze the problem in detail

instead.

Page 116: Javascript Best Practices

Most of the time you’ll find the extra functionality you

need is because of bad planning of your interface.

Page 117: Javascript Best Practices

Don't trust any data

Page 118: Javascript Best Practices

The most important thing about good code is that you cannot trust any data that

comes in.

Page 119: Javascript Best Practices

Don’t believe the HTML document – any user can

meddle with it for example in Firebug.

Page 120: Javascript Best Practices

Don’t trust that data that gets into your function is the right format – test with typeof and

then do something with it.

Page 121: Javascript Best Practices

Don’t expect elements in the DOM to be available – test for

them and that they indeed are what you expect them to

be before altering them.

Page 122: Javascript Best Practices

And never ever use JavaScript to protect

something – it is as easy to crack as it is to code :)

Page 123: Javascript Best Practices

Add functionality with JavaScript, don't create

content.

Page 124: Javascript Best Practices

If you find yourself creating lots and lots of HTML in

JavaScript, you might be doing something wrong.

Page 125: Javascript Best Practices

It is not convenient to create using the DOM...

Page 126: Javascript Best Practices

...flaky to use innerHTML (IE’s Operation Aborted error)...

Page 127: Javascript Best Practices

...and it is hard to keep track of the quality of the HTML you

produce.

Page 128: Javascript Best Practices

If you really have a massive interface that only should be available when JavaScript is

turned on...

Page 129: Javascript Best Practices

...load the interface as a static HTML document via Ajax.

Page 130: Javascript Best Practices

That way you keep maintenance in HTML and allow for customization.

Page 131: Javascript Best Practices

Build on the shoulders of giants

Page 132: Javascript Best Practices

JavaScript is fun, but writing JavaScript for browsers is less

so.

Page 133: Javascript Best Practices

JavaScript libraries are specifically built to make

browsers behave and your code more predictable by plugging browser holes.

Page 134: Javascript Best Practices

Therefore if you want to write code that works without

keeping the maintenance overhead...

Page 135: Javascript Best Practices

... of supporting current browsers and those to come

to yourself...

Page 136: Javascript Best Practices

... start with a good library. (YUI)

Page 137: Javascript Best Practices

Development code is not live code.

Page 138: Javascript Best Practices

Last but not least I want you to remember that some

things that work in other languages are good in

JavaScript, too.

Page 139: Javascript Best Practices

Live code is done for machines.

Page 140: Javascript Best Practices

Development code is done for humans.

Page 141: Javascript Best Practices

Collate, minify and optimize your code in a build process.

Page 142: Javascript Best Practices

Don’t optimize prematurely and punish your developers and those who have to take

over from them.

Page 143: Javascript Best Practices

If we cut down on the time spent coding we have more

time to perfect the conversion to machine code.

Page 144: Javascript Best Practices
Page 145: Javascript Best Practices

Keep in touch:

Christian Heilmann

http://wait-till-i.com

http://scriptingenabled.org

http://twitter.com/codepo8

T H A N K S !

http://delicious.com/codepo8/jscodetips