Maurizio Manetti
"use strict";
Imagine you have some vars and functions
var current = null;
function init(){...}
function change(){...}
function verify(){...}
First step: protect in a namespace
var myNameSpace = {
current:null,
init:function(){...},
change:function(){...},
verify:function(){...}
}
Drawback: to call the functions or change the variable value
you’ll always need to go via the name of the main object
Second step: protect the scope in an anonymous function (aka "the Module Pattern")
var myNameSpace = function(){
var current = null;
function init(){...}
function change(){...}
function verify(){...}
}();
Drawback: functions and vars are now
not accessible from outside the scope
Better: expose "public" methods (and/or properties)
var myNameSpace = function(){
var current = null;
function verify(){...}
return{
init:function(){...}
change:function(){...}
}
}();
Even better: without changing internal syntax
var myNameSpace = function(){
var current = null;
function init(){...}
function change(){...}
function verify(){...}
return{
init:init,
change:change
}
}();
This is the "revealing Module pattern" and
gives you the ability to expose methods with different names
When possible and it makes sense, use the module pattern to protect global scope and have just a few global modules' names with specific and limited responsability
If you don't need any of your variables or functions to be available to the outside, just use an IIFE
(function(){
var current = null;
function init(){...}
function change(){...}
function verify(){...}
})();
"use strict";
"use strict";
– what?"use strict";
to the beginning of a JavaScript file or a JavaScript function (scoped)."use strict";
directive is only recognized at the beginning of a script or a function."use strict";
– changeseval
and arguments
"use strict";
– how?Whole-script strict mode syntax
"use strict";
var v = "Hi! I'm a strict mode script!";
Function-level strict mode syntax
function strict(){
"use strict";
function nested() { return "And so am I!"; }
return "Hi! I'm a strict mode function! " + nested();
}
function notStrict() { return "I'm not strict."; }
Scope level strict mode
// Non-strict code...
(function(){
"use strict";
// Define your library strictly...
})();
"use strict";
– why?"use strict";
– examples 1/3Using a variable, without declaring it, is not allowed:
"use strict";
x = 3.14; // This will cause an error (x is not defined)
Using an object, without declaring it, is not allowed:
"use strict";
x = {p1:10, p2:20}; // This will cause an error (x is not defined)
Deleting a variable (or object) is not allowed.
"use strict";
var x = 3.14;
delete x; // This will cause an error
Deleting a function is not allowed.
"use strict";
function x(p1, p2) {};
delete x; // This will cause an error
"use strict";
– examples 2/3Duplicating a parameter name is not allowed:
"use strict";
function x(p1, p1) {}; // This will cause an error
The string "eval"
cannot be used as a variable
"use strict";
var eval = 3.14; // This will cause an error
The string "arguments"
cannot be used as a variable:
"use strict";
var arguments = 3.14; // This will cause an error
eval()
is not allowed to create variables in the scope from which it was called:
"use strict";
eval ("var x = 2");
alert (x); // This will cause an error
"use strict";
– examples 3/3Future reserved keywords are not allowed in strict mode.
"use strict";
var implements = 1500; // This will cause an error
var interface = 1500; // This will cause an error
var let = 1500; // This will cause an error
var package = 1500; // This will cause an error
var private = 1500; // This will cause an error
var protected = 1500; // This will cause an error
var public = 1500; // This will cause an error
var static = 1500; // This will cause an error
var yield = 1500; // This will cause an error
More examples at w3schools - JavaScript Use Strict
Prefer object literals... | ...to native built-in constructors |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Have exactly one var
statement per scope,
at the top of the scope
/* global vars */
var donald,
mickey = "something";
function foo(a, b, c) {
/* scope foo vars */
var x = 1,
bar,
baz = "something";
}
Constructor approach:
var Counter = function (counter) {
this.counter = counter;
this.increment = function (b) {
this.counter += b;
};
};
Prototype approach:
var Counter = function (counter) {
this.counter = counter;
};
Counter.prototype.increment = function (b) {
this.counter += b;
};
If true … else Shorthand
var big;
if (x > 10) {
big = true;
}
else {
big = false;
}
Can become
var big = (x > 10) ? true : false;
// or even shorter
var big = (x > 10);
Test true values (trivial)
if (likeJavaScript == true)
can be simpler
if (likeJavaScript)
Checking null
or undefined
if (variable1 !== null || variable1 !== undefined || variable1 !== '') {
var variable2 = variable1;
} else {
var variable2 = '';
}
Shorthand
var variable2 = variable1 || '';
Array literals and object literals
// Array
var a = new Array();
a[0] = "myString1";
a[1] = "myString2";
a[2] = "myString3";
// can be shorter
var a = ["myString1", "myString2", "myString3"];
// Object
var skillSet = new Object();
skillSet['Document language'] = 'HTML5';
skillSet['Styling language'] = 'CSS3';
skillSet['Javascript library'] = 'jQuery';
// can be written
var skillSet = {
'Document language' : 'HTML5',
'Styling language' : 'CSS3',
'Javascript library' : 'jQuery',
};
Declaring variables shorthand
var x;
var y;
var z = 3;
Shorthand
var x, y, z = 3;
Assigment operators
x = x+1;
minusCount = minusCount - 1;
y = y*10;
Shorthand
x++;
minusCount --;
y *= 10;
Switch nightmare
switch (something) {
case 1:
doX();
break;
case 2:
doY();
break;
case 3:
doN();
break;
}
Shorthand
var cases = {
1: doX,
2: doY,
3: doN
};
if (cases[something]) {
cases[something]();
}
Create a configuration object that contains all the things that are likely to change over time
config = {
CSS: {
IDs: {
container:'eytp-maincontainer',
sizeControl:'eytp-sizecontrol',
searchField:'eytp-searchfield'
},
classes:{
maxvolume:'maxed',
disabled:'disabled'
}
},
application: {
youtubeAPI:'http://gdata.youtube.com/apiplayer/cl.swf',
volumeChange:10,
searchResults:6
},
locales: {
en: {
volumeMessage:'volume $x percent',
loadingMessage:'Searching, please wait'
},
it: {
volumeMessage:'volume $x per cento',
loadingMessage:'Ricerca in corso, attendere prego'
},
}
}
DOM manipulation is slow: avoid manipulating the DOM in loops
Better:
In a for loop, don't access the length property of an array every time; cache it beforehand.
var myLength = myArray.length;
for ( var i = 0; i < myLength; i++ ) {
// do stuff
}
Looping on a array (using .length)
var fruits = ["apple","banana","orange","mango"];
for(var i = 0; i < fruits.length; i++) {
// do something with fruits[i]
}
Cache length property (static length)
var fruits = ["apple","banana","orange","mango"];
for(var i = 0, max = fruits.length; i < max; i++) {
// do something with fruits[i]
}
Yet another method (reverse while)
var fruits = ["apple","banana","orange","mango"], i = fruits.length;
while(i-=1) { //for readability use i-- (could be slightly slower)
// do something with fruits[i]
}
Method | Chrome (48) | Firefox (44) | IE (11) |
---|---|---|---|
Using list.length | 59,404 | 75,659 | 17,045 |
Using static len | 59,488 | 67,460 | 23,547 |
Using reverse while | 42,205 | 58,891 | 23,634 |
Performance given in K ops/sec
http://jsperf.com/loop-iteration-length-comparison-variations/22The DOM is slow; avoid manipulating it as much as possible. jQuery introduced detach()
in version 1.4 to help address this issue, allowing you to remove an element from the DOM while you work with it.
var table = $( "#myTable" );
var parent = table.parent();
table.detach();
// ... add lots and lots of rows to table
parent.append( table );
jQuery won't tell you if you're trying to run a whole lot of code on an empty selection – it will proceed as though nothing's wrong. It's up to you to verify that your selection contains some elements.
// Bad: This runs three functions before it
// realizes there's nothing in the selection
$( "#nosuchthing" ).slideUp();
// Better:
var elem = $( "#nosuchthing" );
if ( elem.length ) {
elem.slideUp();
}
// Best: Add a doOnce plugin.
jQuery.fn.doOnce = function( func ) {
this.length && func.apply( this );
return this;
}
$( "li.cartitems" ).doOnce(function() {
// make it ajax! \o/
});
This guidance is especially applicable for jQuery UI widgets, which have a lot of overhead even when the selection doesn't contain elements.
Selector optimization is less important than it used to be, as more browsers implement document.querySelectorAll()
and the burden of selection shifts from jQuery to the browser. However, there are still some tips to keep in mind.
ID-Based Selectors
Beginning your selector with an ID is always best.
// Fast:
$( "#container div.robotarm" );
// Super-fast:
$( "#container" ).find( "div.robotarm" );
The .find()
approach is faster because the first selection is handled without going through the Sizzle selector engine – ID-only selections are handled using document.getElementById()
, which is extremely fast because it is native to the browser.
Specificity
Be specific on the right-hand side of your selector, and less specific on the left.
// Unoptimized:
$( "div.data .gonzalez" );
// Optimized:
$( ".data td.gonzalez" );
Use tag.class
if possible on your right-most selector, and just tag or just .class
on the left.
Avoid Excessive Specificity
$( ".data table.attendees td.gonzalez" );
// Better: Drop the middle if possible.
$( ".data td.gonzalez" );
A "flatter" DOM also helps improve selector performance, as the selector engine has fewer layers to traverse when looking for an element.
Avoid the Universal Selector
Selections that specify or imply that a match could be found anywhere can be very slow.
$( ".buttons > *" ); // Extremely expensive.
$( ".buttons" ).children(); // Much better.
$( ".category :radio" ); // Implied universal selection.
$( ".category *:radio" ); // Same thing, explicit now.
$( ".category input:radio" ); // Much better.
Use Stylesheets for Changing CSS on Many Elements
If you're changing the CSS of more than 20 elements using .css()
, consider adding a style tag to the page instead for a nearly 60% increase in speed.
// Fine for up to 20 elements, slow after that:
$( "a.swedberg" ).css( "color", "#0769ad" );
// Much faster:
$( "<style type=\"text/css\">a.swedberg { color: #0769ad }</style>")
.appendTo( "head" );
console.log()
...but there's much more!!
console is not standardized, but they're trying to remedy:
Console living standard
Firefox: Console - Web APIs | MDN
console.assert()
console.clear()
console.count()
console.debug()
console.dir()
console.dirxml()
console.error()
console.group()
console.groupCollapsed()
console.groupEnd()
console.log()
Format specifier
The first parameter you pass to log()
may contain format specifiers, a string token composed of the percent sign (%) followed by a letter that indicates the formatting to be applied.
The following example uses the string (%s) and integer (%d) format specifiers to insert the values contained by the variables userName
and userPoints
:
console.log("User %s has %d points", userName, userPoints);
Styling console output with CSS
console.log("%cThis will be formatted with large, blue text",
"color: blue; font-size: x-large");
console.log()
for basic loggingconsole.error()
and console.warn()
for eye-catching stuffconsole.group()
and console.groupEnd()
to group related messages and avoid clutterconsole.table()
to compare objectsconsole.assert()
to show conditional error messagesconsole.group()
console.group("Authenticating user '%s'", user);
console.log("User authenticated");
console.groupEnd();
// New group for authentication:
console.group("Authenticating user '%s'", user);
// later...
console.log("User authenticated", user);
// A nested group for authorization:
console.group("Authorizing user '%s'", user);
console.log("User authorized");
console.groupEnd();
console.groupEnd();
console.time()
console.time("Array initialize");
var array = new Array(1000000);
for (var i = array.length - 1; i >= 0; i--) {
array[i] = new Object();
};
console.timeEnd("Array initialize");
console
is asyncConsole log output can refer to a different moment in time than when called
function processObject(someObject) {
/* do something complex with object, altering its content */
}
var o = { /* complex object */ };
console.log(o); // before processing it (you get the processed object)
processObject(o);
console.log(o); // after processing it
Actually the content of the log can be exactly the same: only the last value of o get printed
Solution: make a breakpoint (throwing an error or) using debugger
console.log(o); // before processing it
debugger;
processObject(o);
console.log(o); // after processing it
Example
Javascript annoying facts