0% found this document useful (0 votes)
391 views

Good Practices JavaScript

This document provides several tips for writing efficient and optimized JavaScript code. Some key points include: - Use native language parts like arrays and objects instead of constructor functions to avoid overhead. - Cache frequently used values and functions to improve performance. Native functions can be cached to protect their references. - Be cautious with variable scoping and the this keyword. Functions can be bound, called or applied to set context. - Prefer modern language features like addEventListener, classList, and querySelector over older alternatives. - Leverage DOM APIs for tasks like getting computed styles rather than relying only on inline styles. - Optimize rendering by using requestAnimationFrame for intensive DOM operations.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
391 views

Good Practices JavaScript

This document provides several tips for writing efficient and optimized JavaScript code. Some key points include: - Use native language parts like arrays and objects instead of constructor functions to avoid overhead. - Cache frequently used values and functions to improve performance. Native functions can be cached to protect their references. - Be cautious with variable scoping and the this keyword. Functions can be bound, called or applied to set context. - Prefer modern language features like addEventListener, classList, and querySelector over older alternatives. - Leverage DOM APIs for tasks like getting computed styles rather than relying only on inline styles. - Optimize rendering by using requestAnimationFrame for intensive DOM operations.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 112

JavaScript

good practices
and tips and tricks
Damian Wielgosik
ferrante.pl
javascript.pl
twitter.com/varjs
remember that performance tips
change over the time as JVM
evolves
make yourself familiar with
ECMAScript
ECMAScript
sometimes it’s difficult...
... to speak in a human language
... znaleźć ludzki
Dmitry język
Soshnikov

your personal ECMAScript teacher


lint your code, hurt your feelings
jslint.com
jshint.com
/*jslint sloppy: true, vars: true, white: true, plusplus: true,
newcap: true */
(function() {
// ...
})();
mount your linting tool to the
text editor/IDE you use
it’s a result of cmd+L in TextMate
go TDD when it’s possible
try Buster.js
(busterjs.org/)
(function() {
if (typeof require == "function" && typeof module ==
"object") {
buster = require("buster");
require("../models/ArticleFactory.js");
}

var assert = buster.assert;


var factory;

buster.testCase("ArticleFactory", {
setUp: function () {
factory = new app.models.ArticleFactory();
},

"createArticle should not return null": function () {


var article = factory.createArticle(1, "Foobar",
"bla bla", "12/12/2012","12:12",
app.models.Artykul.STATUSES.ACTIVE, { addArticle: function()
{}}, {}, {});
assert.isObject(article);
},
});
})();
there is a lot of unit testing
libraries, choose the best
there are great presentations too

slideshare.net/szafranek/practical-guide-to-unit-
testing
use native language parts for
creating objects and arrays

var arr = []; // not new Array


var obj = {}; // not new Object
use self-invoking functions not
to pollute outer scope

(function() {
// do stuff here
})();
use self-invoking functions not
to pollute outer scope

(function() {
var privateVar = 1;
})();
console.log(privateVar); // undefined
define variables at the top of the
scope

var fn = function() {
var news = [],
timer,
foobar;
};
use === everywhere

0 === ""; // false


0 == ""; // true
do not iterate over an array with
for in loop

Array.prototype.contains = function() {
// ...
};

var arr = [1, 2, 3];


for (var i in arr) {
console.log(i); // contains
}
add to an array using push

var arr = [1];


arr.push(2);
arr; // [1,2]
you can store a global object to
avoid mistakes with this

(function() {
var global = this;
console.log(global === window); // true
})();
global compatible with
ECMAScript 5 and „use strict”

"use strict";
var global = Function('return this')();
cache native functions to protect
them

(function() {
var isArray = Array.isArray;
})();
be careful with this

var obj = {
foo : function() {
return this.myVariable;
},
myVariable : 1
};

var fn = obj.foo;
fn(); // undefined
be careful with this - don’t forget
about call/apply

var obj = {
foo : function() {
return this.myVariable;
},
myVariable : 1
};

var fn = obj.foo;
fn.call(obj); // 1
be careful with this - don’t forget
about bind

var obj = {
foo : function() {
return this.myVariable;
},
myVariable : 1
};

var fn = obj.foo.bind(obj);
fn(obj); // 1
not every browser supports
Function.bind
not every browser supports
Function.bind - but you can
polyfill it!
Function.bind polyfill

if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is
not callable");
}

var aArgs = Array.prototype.slice.call(arguments, 1),


fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

return fBound;
};
}
for loop is not slower than while

jsperf.com/for-loop-research
using indexes with localStorage
is faster than getItem in some
browsers

jsperf.com/localstorage-science
using indexes with localStorage
is faster than getItem in some
browsers

var key = 1;
localStorage[key]; // faster
localStorage.getItem(key); // slower
native forEach is not faster than
classic loops

var arr = [1, 2, 3, 4, 5];


arr.forEach(function() {}); // slower
for (var i = 0, ilen = arr.length; i < ilen; i++) {}; // faster

jsperf.com/for-vs-array-foreach
use requestAnimationFrame if you deal with
heavy proccesses like DOM manipulation

(function() {
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame || function(fn) { window.setTimeout(fn,
16); };
window.requestAnimationFrame = requestAnimationFrame;

var start = Date.now().


var elements = 0;

var step = function() {


var house = document.createElement("div");
house.classList.addClass("house");
document.body.appendChild(building);
elements++;
if (elements < 10000) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
})();
do not create too many functions
- that costs memory

window.setTimeout(function() {
makeThings();
}, 16);

no!

var frame = function() {


makeThings();
};
window.setTimeout(frame, 16);

yes!
functions are objects

var fn = function() {};


fn.myProperty = 1;
fn.myProperty; // 1
functions are objects, so they
help cache

var getSin = function(num) {


if (getSin[num]) {
return getSin[num];
} else {
return getSin[num] = Math.sin(num);
}
};
more crossbrowser isArray?

(function() {
var toString = Object.prototype.toString;
var isArray = Array.isArray || function(arr) { return toString.call
(arr) === "[object Array]"; };
})();
clear arrays using length for
memory performance

var arr = [1, 2, 3, 4, 5];


arr.length = 0; // instead of arr = [];
max number in an array?

var arr = [1, 2, 3, 4, 5];


Math.max.apply(Math, arr);
use ”in” to check if the property
is defined in the object

var obj = {
foobar : ""
};

'foobar' in obj; // true


'barbar' in obj; // false
!!obj.foobar; // false
use typeof to check variable
types

var num = 2;
if (typeof num === "number") {
// we have a number
}
use typeof to check if you can
fire a function

var fn = function() {};


if (typeof fn === "function") {
fn();
}
typeof RegExp

typeof RegExp === "function"; // true


typeof /\d+/g; // "object"
you don’t need jQuery just to get
some nodes

var news = document.querySelectorAll("div.news"); // finds all the elements


var news = document.querySelector("div.news"); // finds the first element
you don’t need regular
expressions to work with class
names

document.body.classList.addClass("foo"); // <body class="foo">


document.body.classList.contains("foo"); // true
document.body.classList.toggle("foo"); // <body>
document.body.classList.toggle("foo"); // <body class="foo">
document.body.classList.remove("foo"); // <body>
build your view templates with
innerHTML

var html = '<ul class="news">' +


'<li>' +
'<h1>Something</h1>' +
'<p>Text</p>' +
'</li>' +
'</ul>';

container.innerHTML = html;
var newsList = container.querySelector(".news");
make use of datasets

<div data-id="123" data-type="news"></div>

node.dataset.id; // "123"
node.dataset.type; // "news"
did you know about
node.matchesSelector?

<div id="foo" class="bar">This is the element!</div>


<script type="text/javascript">
var el = document.getElementById("foo");
if (el.mozMatchesSelector("div.bar")) {
alert("Match!");
}
</script> // thanks to MDN
window.getComputedStyle if
node.style is not enough

<style>
#elem-container{
position: absolute;
left: 100px;
top: 200px;
height: 100px;
}
</style>

<div id="elem-container">dummy</div>
<div id="output"></div>

<script>
function getTheStyle(){
var elem = document.getElementById("elem-container");
var theCSSprop = window.getComputedStyle(elem,null).getPropertyValue("height");
document.getElementById("output").innerHTML = theCSSprop;
}
getTheStyle();
</script>
innerHTML is not the fastest
way to check if node is empty

if (node.innerHTML === "") {} // slower


if (node.childNodes.length === 0) {} // faster
don’t extend DOM nodes with
custom properties to avoid
memory leaks

var div = document.createElement("div");


div.customThing = function() {};
add events using
addEventListener

node.addEventListener("click", function() {
alert("Boom!");
}, false);
do not forget to prevent form
submitting when dealing with
web app forms that are sent e.g.
by AJAX

form.addEventListener("submit", function(e) {
e.preventDefault();
}, false);
Performance?
var start = +new Date();
for (var i = 0; i < 100000; i++);
console.log("Result is: ", +new Date() - start);
Better?
console.time("My test");
for (var i = 0; i < 100000; i++);
console.timeEnd("My test");
Still better?
console.profile("My test");
runApp();
console.profileEnd("My test");
The best?
jsperf.com

jsperf.com
jsperf measures operations per
second!
See also window.performance
do not optimize prematurely!
JavaScript !== Java

do not optimize prematurely!

flickr.com/photos/paulmartincampbell/3583176306/sizes/o/in/photostream/
forget about your old habits!
do not port bad solutions to JavaScript!
otherwise they’re gonna find
you! ;-)

http://www.fotofaza.pl/podglad_zdjecia,15,ludzie,chuligani.html
function calls cost time!
use JS asynchronously when needed
var arr = [ function() { console.log("A"); },
function() { throw new Error("boom!"); },
function() { console.log("B"); },
function() { console.log("C"); }
];

for (var i = 0, ilen = arr.length; i < ilen; i++) {


arr[i]();
}
oops?
var arr = [ function() { console.log("A"); },
function() { throw new Error("boom!"); },
function() { console.log("B"); },
function() { console.log("C"); }
];

for (var i = 0, ilen = arr.length; i < ilen; i++) {


window.setTimeout(arr[i], 0);
}
timers can be useful with AJAX requests
var throttle = function(fn, delay) {
var timer = null;
return function () {
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
};

$('input.username').keypress(throttle(function (event) {
// do the Ajax request
}, 250));

http://remysharp.com/2010/07/21/throttling-function-calls/
parseInt(„09”) === 0
JS thinks „09” is an octal number
because it starts with 0
parseInt(„09”, 10) === 9
However,
parseFloat(„09”) === 9
However,
parseFloat(„09”) === 9
document.querySelectorAll("div")
returns a NodeList
not an array
var nodes = document.querySelectorAll("div");
nodes = [].slice.apply(nodes);
However:
„Whether the slice function can be applied successfully to a host object is
implementation-dependent.” - ECMAScript
mobile?
Use window.scrollTo(0, 1) to get rid of the browser address bar
on iOS!
/mobile/i.test(navigator.userAgent) && !location.hash && setTimeout
(function () {
if (!pageYOffset) window.scrollTo(0, 1);
}, 1000);

thanks to amazing work by Remy Sharp


http://remysharp.com/2010/08/05/doing-it-right-skipping-the-
iphone-url-bar/
Mobile debugging?
Aardwolf - mobile
debugging made easy

Yes, we all know Firebug and Web Inspector


Memory stats?
WebKit Inspector

Memory?
Memory leaks?
Memory leak checker
sIEeve

Performance?

home.orange.nl/jsrosman/
Memory leaks?

people.mozilla.com/~dbaron/leak-screencasts/
general thoughts
Get rid of jQuery if it’s not neccessary -
there is http://microjs.com
Hunt on new stuff!
http://js.gd might be a good start!
Visit JSNews on Facebook for more awesomeness
http://tinyurl.com/jsnewspl
or attend meet.js meetups
Poznan, Warsaw, Wroclaw, Cracow
http://meetjs.pl
but first of all, be smart and listen to smart people -
there is a lot on the web

You might also like