responsible javascript
TRANSCRIPT
Responsible JavaScriptRefresh NYC — January 2010
1
Where should I get started?
• Globals
• Namespaces
• Prototypes
• Factories
• this
• Incrementing & Decrementing
• switch () {...}
• Equality
• $
2
Globals
http://www.lovemikeg.com/blog/2010/01/19/responsible-javascript-globals/
3
Globals have a bad reputation.
4
Where possible, just avoid them.
5
(function () {
var printMe;
printMe = document.getElementById(‘print-me’); printMe.addEventListener(‘click’, function () { window.print(); return false; }, false);
})();
6
In all other cases, simply don’t pollute.
7
Rules for Responsible Globals
Namespace everything.
Only one global per library.
Only one global per application.
8
Good libraries...
When choosing a library, choose a responsible one.
• jQuery w/compatibility mode is great
• YUI is also a great choice
9
Good applications...
Keep it simple: each app gets a global
In most cases, a site == an app
In other cases, a page == an app
• searchResults
• userProfile
• shoppingCart
10
Namespaces
http://www.lovemikeg.com/blog/2010/01/20/responsible-javascript-namespaces/
11
Namespaces are a really good idea.
12
jQuery.ajax(…)
13
Namespaces can get out of control. Fast.
14
com.foo.utils.appManager .myApp.widgets .contactForm.validate();
15
Responsible Namespaces
Use a namespace creator function
Segregate application namespaces from library namespaces
Organize libraries into component groups
Organize applications into functional groups
16
This might seem like a good idea…
var myNS = { … };
…until you have multiple modules in myNS.
17
How do you do this reliably?
myNS.myModule = { … };
18
How do you do this reliably?
var myNS = window.myNS || {};myNS.myModule = {};
19
This is a better idea:
foo.namespace(‘myNS.myModule’);
20
Applications vs. Libraries
Applications are living, breathing structures which must be accepting to change both internally and externally.
Libraries are shared dependencies and must be be organized in a stable structure so as to minimize the need for change at all.
21
In other words...
You application will be a self-contained entity which is made up of lots of smaller components.
• Don’t be strict and rigid with your app namespaces.
• References to other apps are common, keep app namespaces shallow.
22
Prototypes
http://www.lovemikeg.com/blog/2010/01/21/responsible-javascript-prototype-modification/
23
Prototypes are powerful. Really powerful.
24
“With great power there must also come… great responsibility!”
— Amazing Fantasy #15 (The first Spider-Man story)
25
“Yeah, but I want convenience.”
Array.prototype.each = function (callback) { for (var i = 0, l = this.length; i < l; i++) { callback(this[i], i); }};
26
Think it through…
Is it proper to cast your dependencies on the language itself?
How/Where is this documented? Is it obvious to your team?
Are there any other unintended consequences?
27
How many members in foo?
Object.prototype.count = function () { var count = 0; for (var i in this) count++; return count;};
console.log( {'foo':12345}.count());
28
Go ahead. Say it.
“But you should be using Object.hasOwnProperty.”
• Yeah but does anyone actually do that?
• Do you feel comfortable assuming that all future developers on your project will know that?
29
A better option is to simply extend.
30
var MyArray = function () {};MyArray.prototype = new Array;
31
Factories
http://www.lovemikeg.com/blog/2010/01/22/responsible-javascript-using-factories/
32
Factories are your friends.
33
Factories manage complexity.
…and anything that reduces complexity should be considered a best practice right?
Factories can also:
• Make your code safer
• Make your code faster
34
Factories are a safety measure.
var Person = function (name, location) { this.name = name; this.location = location;};
var mike = new Person('Mike G.', 'NYC');var alex = Person('Alex H.', 'NYC'); // oops!
35
Constructors are simply broken.
Forgetting the new keyword can be disastrous.
Best case scenario, a few globals spill out.
Worst case scenario, your app stops working.
36
Problem solved.
Person.factory = function (name, location) { return new Person(name, location);}
var mike = new Person.factory('Mike G.', 'NYC');var alex = Person.factory('Alex H.', 'NYC');
37
this
http://www.lovemikeg.com/blog/2010/01/23/responsible-javascript-using-this/
38
this has an identity crisis.
39
var foo = { ‘bar’ : 12345,
‘getBar’ : function () { return this.bar; },
‘onclickCallback’ : function () { window.open(this.href); return false; },
‘wtf’ : function () { return { ‘hello’ : ‘world’, ‘sayHi’ : function () { return this.hello; } }; }};
40
this is ambiguous
It refers to the object in which its defined.
Unless:
• It’s copied (referenced) to another object
• It’s used as an event handler (sometimes).
• You .call or .apply it.
41
Best practices
Know your namespace. Be explicit.
Fix your events. You can never be too sure.
Keep your event handlers in a separate object.
Document all irregularities (including event handlers).
42
Incrementing & Decrementing
43
++ and -- have a bad reputation… to some.
44
“The ++ (increment) and -- (decrement) operators have been known to contribute to bad code by encouraging excessive trickiness. They are second only to faulty architecture in enabling to viruses and other security menaces.”
—Douglas Crockfordhttp://www.jslint.com/lint.html#inc
45
Trickiness like...
var a = 1;var b = 2;var c = 3;
console.log(a++ + b == c);console.log(a + ++b == c);console.log(a + + + b == c);
console.log(a, b, c);
46
Suggestions
Don’t write tricky code like that.
• Probably your best bet ;)
Don’t use incrementors/decrementors
• There’s nothing wrong with += 1
47
switch () {...}
48
switch can break if you don’t break.
49
function dateSuffix(date) { switch (date){ case (1) : case (21) : case (31): dt = "st"; break;
case (2) : case (22): dt = "nd"; break;
case (3) : case (23): dt = "rd"; break;
default: dt = "th"; } return date + suffix;}
50
… is the same as…
51
function betterDateSuffix (date) { if (date === 1 || date === 21 || date === 31) { return date + 'st'; } else if (date === 2 || date === 22) { return date + 'nd'; } else if (date === 3 || date === 23) { return date + rd; } else { return date + 'th'; } return date + suffix;}
52
and …
53
function yetAnotherDateSuffix (date) { var suffixMap = { 'st' : (date === 1 || date === 21 || date === 31), 'nd' : (date === 2 || date === 22), 'rd' : (date === 3 || date === 23), 'th' : true }; for (var suffix in suffixMap) { if (suffixMap[suffix]) { return date + suffix; } }}
54
Which is better?
Depends on your team’s coding standards.
I suggest:
• The one which is easiest to understand.
• The one which is easiest to change.
(I like the third option, personally.)
55
Equality
56
Equality isn’t as easy as you think.
57
Take your best guess
console.log(false == -1);console.log(false == -0);console.log(false == 0);console.log(false == []);console.log(false == "");console.log(false == "0");console.log(false == null);console.log(false == undefined);console.log(false == null);
58
Take your best guess
console.log(false == -1); // falseconsole.log(false == -0); // trueconsole.log(false == 0); // trueconsole.log(false == []); // trueconsole.log(false == ""); // trueconsole.log(false == "0"); // trueconsole.log(false == null); // falseconsole.log(false == undefined); // falseconsole.log(false == null); // false
59
Just use === or !==
Your code will be less ambiguous
Your code will run faster
• No need to convert types
60
$
61
Developers love $.
62
The problem, is that everyone loves $.
Brought to you by Prototype
Made famous by jQuery
But what happens if you include jQuery and Prototype on the same page?
63
The Specs say...
“$ is reserved for implementations”
• Whatever that means.
64
Fact: $ is perfectly valid.
65
“Just don’t do that.”
Yeah. Easier said than done.
• Try talking your client its better to rewrite the widget written in another library
• They like their $$ too.
66
Just don’t be irresponsible.
Give your devs a choice to use it or not
Never write applications that assume $
Always assume control over $
67
There is hope.
jQuery has compatability mode.
• Please use it.
• Develop your apps as if you’re writing a plugin…
YUI is a nice choice too ;)
var myApp = (function ($) { // Your code goes here.})(window.jQuery);
68