第1页
JavaScript “Best Practices”
Christian Heilmann | http://wait-till-i.com | http://scriptingenabled.org Bangalore, India, Yahoo internal training, February 2009
第2页
“Best Practice” presentations are hard to do.
第3页
Trying to tell developers to change their ways and follow your example is a tough call.
第4页
I also consider it a flawed process.
第5页
I am bored of discussions of syntax details.
第6页
This is not about forcing you to believe what I believe.
第7页
This is about telling you what worked for me and might as
well work for you.
第8页
I’ve collected a lot of ideas and tips over time how to be
more effective.
第9页
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
第10页
Make it understandable!
第11页
Choose easy to understand and short names for variables
and functions.
第12页
Bad variable names:
x1 fe2 xbqne
第13页
Also bad variable names:
incrementorForMainLoopWhichSpansFromTenToTwenty createNewMemberIfAgeOverTwentyOneAndMoonIsFull
第14页
Avoid describing a value with your variable or function name.
第15页
For example isOverEighteen()
might not make sense in some countries,
isLegalAge() however works everywhere.
第16页
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.
第17页
Avoid globals
第18页
Global variables are a terribly bad idea.
第19页
You run the danger of your code being overwritten by any other JavaScript added to
the page after yours.
第20页
The workaround is to use closures and the module
pattern.
第21页
var current = null; var labels = [
‘home’:’home’, ‘articles’:’articles’, ‘contact’:’contact’ ]; function init(){ }; function show(){ }; function hide(){ };
第22页
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
第23页
Problem: access is not contained, anything in the page can overwrite what you
do.
第24页
demo = { current:null, labels:[
Object Literal: Everything is
‘home’:’home’,
contained but can be
‘articles’:’articles’, ‘contact’:’contact’ ],
accessed via the object name.
init:function(){
},
show:function(){
demo.current = 1;
},
hide:function(){
demo.show();
}
}
第25页
Problem: Repetition of module name leads to huge
code and is annoying.
第26页
(function(){ var current = null; var labels = [
Anonymous Module: Nothing is global.
‘home’:’home’,
‘articles’:’articles’,
‘contact’:’contact’
];
function init(){
};
function show(){
current = 1;
};
function hide(){
show();
};
})();
第27页
Problem: No access from the outside at all (callbacks, event handlers)
第28页
module = function(){ var labels = [ ‘home’:’home’,
Module Pattern: You need to specify
‘articles’:’articles’, ‘contact’:’contact’ ];
what is global and what isn’t – switching
return {
syntax in between.
current:null,
init:function(){
},
show:function(){
module.current = 1;
},
hide:function(){
module.show();
}
}
}();
第29页
Problem: Repetition of module name, different syntax for inner functions.
第30页
module = function(){
var current = null;
Revealing Module
var labels = [ ‘home’:’home’, ‘articles’:’articles’,
Pattern: Keep consistent
‘contact’:’contact’
syntax and mix and
]; function init(){ };
match what to make global.
function show(){
current = 1;
};
function hide(){
show();
};
return{init:init,show:show,current:current}
}();
第31页
module = function(){
var current = null;
Revealing Module
var labels = [ ‘home’:’home’, ‘articles’:’articles’,
Pattern: Keep consistent
‘contact’:’contact’
syntax and mix and
]; function init(){ };
match what to make global.
function show(){
current = 1;
};
function hide(){
show();
};
return{init:init,show:show,current:current}
}();
module.init();
第32页
Stick to a strict coding style
第33页
Browsers are very forgiving JavaScript parsers.
第34页
However, lax coding style will hurt you when you shift to
another environment or hand over to another developer.
第35页
Valid code is good code.
第36页
Valid code is secure code.
第37页
Validate your code: http://www.jslint.com/
第38页
TextMate users: get Andrew’s JavaScript Bundle:
http://andrewdupont.net/ 2006/10/01/javascript-tools-
textmate-bundle/
第39页
Comment as much as needed but no more
第40页
Comments are messages from developer to developer.
第41页
“Good code explains itself” is an arrogant myth.
第42页
Comment what you consider needed – but don’t tell others
your life story.
第43页
Avoid using the line comment though. It is much safer to use /* */ as that doesn’t cause errors when the line break is removed.
第44页
If you debug using comments, there is a nice
little trick:
第45页
module = function(){ var current = null; function init(){ };
/* function show(){ current = 1; }; function hide(){ show(); };
*/ return{init:init,show:show,current:current}
}();
第46页
module = function(){ var current = null; function init(){ };
/* function show(){ current = 1; }; function hide(){ show(); };
// */ return{init:init,show:show,current:current}
}();
第47页
module = function(){ var current = null; function init(){ };
//* function show(){ current = 1; }; function hide(){ show(); };
// */ return{init:init,show:show,current:current}
}();
第48页
Comments can be used to write documentation – just
check the YUI doc:
http://yuiblog.com/blog/ 2008/12/08/yuidoc/
第49页
However, comments should never go out to the end user in plain HTML or JavaScript.
第50页
Back to that later :)
第51页
Avoid mixing with other technologies
第52页
JavaScript is good for calculation, conversion, access to outside sources (Ajax) and to define the behaviour of an interface
(event handling).
第53页
Anything else should be kept to the technology we have to
do that job.
第54页
For example:
Putaredborderaroundallfields withaclassof“mandatory”when theyareempty.
第55页
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'; } }
第56页
Two month down the line:
Allstyleshavetocomplywiththe newcompanystyleguide,no bordersareallowedanderrors shouldbeshownbyanalerticon nexttotheelement.
第57页
People shouldn’t have to change your JavaScript code to change the look and feel.
第58页
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’; } }
第59页
Using classes you keep the look and feel to the CSS designer.
第60页
Using CSS inheritance you can also avoid having to loop over a lot of elements.
第61页
Use shortcut notations
第62页
Shortcut notations keep your code snappy and easier to
read once you got used to it.
第63页
var cow = new Object(); cow.colour = ‘white and black’; cow.breed = ‘Holstein’; cow.legs = 4; cow.front = ‘moo’; cow.bottom = ‘milk’;
is the same as
var cow = { colour:‘white and black’, breed:‘Holstein’, legs:4, front:‘moo’, bottom = ‘milk’
};
第64页
var lunch = new Array(); lunch[0]=’Dosa’; lunch[1]=’Roti’; lunch[2]=’Rice’; lunch[3]=’what the heck is this?’;
is the same as
var lunch = [ ‘Dosa’, ‘Roti’, ‘Rice’, ‘what the heck is this?’
];
第65页
if(v){ var x = v;
} else { var x = 10;
} is the same as
var x = v || 10;
第66页
var direction; if(x > 100){
direction = 1; } else {
direction = -1;
} is the same as
var direction = (x > 100) ? 1 : -1;
/* Avoid nesting these! */
第67页
Modularize
第68页
Keep your code modularized and specialized.
第69页
It is very tempting and easy to write one function that does everything.
第70页
As you extend the functionality you will however find that you do the same things in several
functions.
第71页
To prevent that, make sure to write smaller, generic helper
functions that fulfill one specific task rather than
catch-all methods.
第72页
At a later stage you can also expose these when using the revealing module pattern to create an API to extend the
main functionality.
第73页
Good code should be easy to build upon without re-writing
the core.
第74页
Enhance progressively
第75页
There are things that work on the web.
第76页
Use these rather than creating a lot of JavaScript
dependent code.
第77页
DOM generation is slow and expensive.
第78页
Elements that are dependent on JavaScript but are
available when JavaScript is turned off are a broken promise to our users.
第79页
Example: TV tabs.
第80页
Tab Interface
第81页
Tab Interface
第82页
Allow for configuration and translation.
第83页
Everything that is likely to change in your code should not be scattered throughout
the code.
第84页
This includes labels, CSS classes, IDs and presets.
第85页
By putting these into a configuration object and making this one public we make maintenance very easy and allow for customization.
第86页
carousel = function(){ var config = { CSS:{ classes:{ current:’current’, scrollContainer:’scroll’ }, IDs:{ maincontainer:’carousel’ } } labels:{ previous:’back’, next:’next’, auto:’play’ } settings:{ amount:5,
第87页
skin:’blue’, autoplay:false } }; function init(){ }; function scroll(){ }; function highlight(){ }; return {config:config,init:init} }();
第88页
Avoid heavy nesting
第89页
Code gets unreadable after a certain level of nesting –
when is up to your personal preference and pain threshold.
第90页
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...).
第91页
You can avoid heavy nesting and loops inside loops with specialized tool methods.
第92页
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);
}
第93页
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; }
第94页
Think of bad editors and small screens.
第95页
Optimize loops
第96页
Loops can get terribly slow in JavaScript.
第97页
Most of the time it is because you’re doing things in them
that don’t make sense.
第98页
var names = ['George','Ringo','Paul','John']; for(var i=0;i<names.length;i++){
doSomeThingWith(names[i]); }
第99页
This means that every time the loop runs, JavaScript
needs to read the length of the array.
第100页
You can avoid that by storing the length value in a different
variable:
第101页
var names = ['George','Ringo','Paul','John']; var all = names.length; for(var i=0;i<all;i++){
doSomeThingWith(names[i]); }
第102页
An even shorter way of achieving this is to create a second variable in the pre-
loop condition.
第103页
var names = ['George','Ringo','Paul','John']; for(var i=0,j=names.length;i<j;i++){
doSomeThingWith(names[i]); }
第104页
Keep computation-heavy code outside of loops.
第105页
This includes regular expressions but first and foremost DOM manipulation.
第106页
You can create the DOM nodes in the loop but avoid
inserting them to the document.
第107页
Keep DOM access to a minimum
第108页
If you can avoid it, don’t access the DOM.
第109页
The reason is that it is slow and there are all kind of
browser issues with constant access to and changes in the
DOM.
第110页
Write or use a helper method that batch-converts a dataset
to HTML.
第111页
Seed the dataset with as much as you can and then call the method to render all
out in one go.
第112页
Don't yield to browser whims!
第113页
What works in browsers today might not tomorrow.
第114页
Instead of relying on flaky browser behaviour and
hoping it works across the board...
第115页
...avoid hacking around and analyze the problem in detail
instead.
第116页
Most of the time you’ll find the extra functionality you
need is because of bad planning of your interface.
第117页
Don't trust any data
第118页
The most important thing about good code is that you cannot trust any data that
comes in.
第119页
Don’t believe the HTML document – any user can meddle with it for example in
Firebug.
第120页
Don’t trust that data that gets into your function is the right format – test with typeof and
then do something with it.
第121页
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.
第122页
And never ever use JavaScript to protect something – it is as easy to crack as it is to code :)
第123页
Add functionality with JavaScript, don't create
content.
第124页
If you find yourself creating lots and lots of HTML in JavaScript, you might be doing something wrong.
第125页
It is not convenient to create using the DOM...
第126页
...flaky to use innerHTML (IE’s Operation Aborted error)...
第127页
...and it is hard to keep track of the quality of the HTML you
produce.
第128页
If you really have a massive interface that only should be available when JavaScript is
turned on...
第129页
...load the interface as a static HTML document via Ajax.
第130页
That way you keep maintenance in HTML and allow for customization.
第131页
Build on the shoulders of giants
第132页
JavaScript is fun, but writing JavaScript for browsers is less
so.
第133页
JavaScript libraries are specifically built to make browsers behave and your code more predictable by plugging browser holes.
第134页
Therefore if you want to write code that works without keeping the maintenance overhead...
第135页
... of supporting current browsers and those to come
to yourself...
第136页
... start with a good library. (YUI)
第137页
Development code is not live code.
第138页
Last but not least I want you to remember that some things that work in other languages are good in JavaScript, too.
第139页
Live code is done for machines.
第140页
Development code is done for humans.
第141页
Collate, minify and optimize your code in a build process.
第142页
Don’t optimize prematurely and punish your developers and those who have to take
over from them.
第143页
If we cut down on the time spent coding we have more
time to perfect the conversion to machine code.
第145页
THANKS!
Keep in touch: Christian Heilmann http://wait-till-i.com http://scriptingenabled.org http://twitter.com/codepo8
http://delicious.com/codepo8/jscodetips