Top Banner
1 Writing Efficient JavaScript Code Prem Nawaz Khan
44

Effecient javascript

Jan 15, 2015

Download

Documents

mpnkhan

Writing Efecient
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Effecient javascript

1

Writing Efficient JavaScript CodePrem Nawaz Khan

Page 2: Effecient javascript

2

What is this Talk about?

• Not an introduction to JavaScript

• This is about telling you what worked for me and might as well work for you.

• Some collection of ideas and tips over a period of time

Page 3: Effecient javascript

3

Optimization

• Premature optimization is the root of all evil. — Donald Knuth

• Do not worry about optimization until you have the application working correctly.

• If it isn’t right, it doesn’t matter if it is fast.

• Clean, correct code is easier to optimize.

• Sometimes restructuring or redesign is required.

Page 4: Effecient javascript

4

Some Basics• Make your code understandable

• Strict to PayPal JavaScript coding standards

• Comment as much as needed

• Use shortcut notations

• Allow for configuration

Page 5: Effecient javascript

5

Standards

• Valid code is good code

• Good code is secure code (most cases)

• Good code is easy to Optmize

http://webdev.paypal.com/standards/coding-standards/javascript-standards

Page 6: Effecient javascript

6

Comments

• Comments are messages from developer to developer

• “Good code explains itself” is a myth

• Comment as much as needed – but no need to do story telling

• // Breaks when line break is removed

• Use /* comments */

Page 7: Effecient javascript

7

Comments (Contd.)Testing blocks of code

getRadioSelected:function (radioGroup)

{ /**/var selected = "";for (var i=0; i< radioGroup.length; i++) {

if (radioGroup[i].checked) {selected = radioGroup[i].value;

}}

return selected;

/**/

/** /

for (var i = 0,j=radioGroup.length; i < j; i++)

{ if (radioGroup[i].checked) { return radioGroup[i].value }

}return '';/**/

},

Page 8: Effecient javascript

8

Comments (Contd.)getRadioSelected:function (radioGroup)

{ /** /var selected = "";for (var i=0; i< radioGroup.length; i++) {

if (radioGroup[i].checked) {selected = radioGroup[i].value;

}}

return selected;

/**/

/** /

for (var i = 0,j=radioGroup.length; i < j; i++)

{ if (radioGroup[i].checked) { return radioGroup[i].value }

}return '';/**/

},

Page 9: Effecient javascript

9

Comments (Contd.)getRadioSelected:function (radioGroup)

{ /** /var selected = "";for (var i=0; i< radioGroup.length; i++) {

if (radioGroup[i].checked) {selected = radioGroup[i].value;

}}

return selected;

/**/

/**/

for (var i = 0,j=radioGroup.length; i < j; i++)

{ if (radioGroup[i].checked) { return radioGroup[i].value }

}return '';/**/

},

Page 10: Effecient javascript

10

JS Optimization

Let’s Optimize some JavaScript…

Page 11: Effecient javascript

11

Shortcuts

var cow = new Object();

cow.colour = ‘white and black’;

cow.breed = ‘Jersey’;

cow.legs = 4;

is same asvar cow =

{

colour:‘white and black’,

breed:‘Jersey’,

legs:4

};

Page 12: Effecient javascript

12

Shortcuts (Contd.)

var lunch = new Array();

lunch[0]=’Dosa’;

lunch[1]=’Roti’;

lunch[2]=’Rice’;

lunch[3]=’idli’;

is same asvar lunch = [

‘Dosa’,

‘Roti’,

‘Rice’,

‘idli’

];

Page 13: Effecient javascript

13

Shortcuts (Contd.)var direction;if (x > 100){

direction = 1;} else {

direction = -1;}

is same as

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

/* Avoid nesting these! */

Page 14: Effecient javascript

14

Shortcuts (Contd.)function getWidth(clientWidth)

{

var width;

if(typeof clientWidth != 'undefined')

{

width=clientWidth;

}

else

{

width=100;}

}

is same as

function getWidth(clientWidth)

{

var width = clientWidth || 100;

}

Page 15: Effecient javascript

15

Common subexpression removal

Consider:

var s = “hello, world”;

for (var i = 0; i < s.length; i++) {

// ...

}

Better way to write:

var s = “hello, world”, i, n;

for (i = 0, n = s.length; i < n; i++) {

// ...

}

Page 16: Effecient javascript

16

Cache Function Pointers

• Consider:function doMyDuty(collection)

{

var i, n = collection.length;

for (i = 0; i < n; i++)

doStuff(collection[i]);

}

• Here doStuff is a function outside the scope of

doMyDuty()

Page 17: Effecient javascript

17

Cache Function Pointers (Contd.)

The global lookup for every iteration can be

avoided by rewriting the doMyDuty as:

function doMyDuty(collection)

{var i, n = collection.length,

fcn = doStuff;

for (i = 0; i < n; i++)

fcn(collection[i]);

}

Page 18: Effecient javascript

18

Global variables are evil

• Crawling up the scope chain

• Memory de-allocation only at end

• Can be overridden

Page 19: Effecient javascript

19

Global variables are evil (Contd.)

var a = 1;

(function(){

var a = 2;function b(){var a = 3;alert(a);

}

b();

})(); // 3

Page 20: Effecient javascript

20

Global variables are evil (Contd.)

var a = 1;

(function(){

var a = 2;function b(){//var a = 3;alert(a);

}

b();

})(); // 3

Page 21: Effecient javascript

21

Global variables are evil (Contd.)

var a = 1;

(function(){

//var a = 2;function b(){//var a = 3;alert(a);

}

b();

})(); // 3

Page 22: Effecient javascript

22

Avoid try/catch inside loops

The try/catch block creates a local scope and creates the exception object at runtime that lives in that scope.

Consider:

for ( // ... ) {

try

{ // ...

} catch (e) { // ...}

}

Page 23: Effecient javascript

23

Avoid try/catch inside loops (Contd.)

Right way:

try {

for ( // ... ) {

// ...

}

} catch (e) {// ...}

Page 24: Effecient javascript

24

JavaScript & DOM

DOM

• Different browsers perform differently

• Browserscope- It is a community-driven project for profiling web browsers.

http://www.browserscope.org/

Page 25: Effecient javascript

25

JavaScript & DOM

• The bottleneck tends to be the DOM interface.

• There is a significant cost every time you touch the DOM tree.

• document.getElementsByTagName('*').length in firebug console gives number of DOM elements in that page. Reduce the number of DOM elements as much as possible.

Page 26: Effecient javascript

26

JavaScript & DOM

• Each touch can result in a reflow computation, which is expensive.

• It is faster to manipulate new nodes before they are attached to the tree.

• Touching unattached nodes avoids the reflow cost.

• Setting innerHTML does an enormous amount of work, but browsers are really good at it, and it only touches the DOM once.

http://www.quirksmode.org/dom/innerhtml.html

Page 27: Effecient javascript

27

JavaScript & DOMReplace HTML faster than innerHTML

function replaceHtml(el, html) {

var oldEl = typeof el === "string" ? document.getElementById(el) : el;

/*@cc_on // Pure innerHTML is slightly faster in IE

oldEl.innerHTML = html;

return oldEl;

@*/

var newEl = oldEl.cloneNode(false);

newEl.innerHTML = html;

oldEl.parentNode.replaceChild(newEl, oldEl);

/* Since we just removed the old element from the DOM, return a reference

to the new element, which can be used to restore variable references. */

return newEl;

};

Eg. http://dev.paypal.com/~pkhan/examples/replaceHTML.html

Source : http://blog.stevenlevithan.com/archives/faster-than-innerhtml

Page 28: Effecient javascript

28

Updating DOM

Before:-

function foo() {

for (var count = 0; count < 1000; count++) {

document.body.innerHTML += 1;

}

}

Page 29: Effecient javascript

29

Updating DOM (Contd).

After:-

function foo() {

var inner = '';

for (var count = 0; count < 1000; count++) {

inner += 1;

}

document.body.innerHTML += inner;

} //1000 times faster

Demo :-http://dev.paypal.com/~pkhan/examples/Domtouch.html

Page 30: Effecient javascript

30

Cache Property access

Before:-For example, when making several changes to styles for an element:

this.iframe.style.top = xy[1] +"px";  

this.iframe.style.left = xy[0] +"px";

this.iframe.style.height = elem.clientHeight +"px";

this.iframe.style.width = elem.clientWidth +"px";

this.iframe.style.visibility = "visible";

this.iframe.style.zIndex = "1";

Page 31: Effecient javascript

31

Cache property access (Contd.)

After:-var d = this.iframe;

e = d.style; //Cache style

e.top = xy[1] +"px";  

e.left = xy[0] +"px";

e.height = elem.clientHeight +"px";

e.width = elem.clientWidth +"px";

e.visibility = "visible";

e.zIndex = "1";

Page 32: Effecient javascript

32

Cache property access (Contd.)

This is better than the other 2var d = this.iframe,

style;

style='top:'+xy[1] +'px;left:'+xy[0] +'px;height:'+ elem.clientHeight +'px;width:'+elem.clientHeight +'px;visibility:visible;z-Index:1;';

if (typeof d.style.cssText != “undefined”) {

d.style.cssText = style;

} else {

d.setAttribute(“style”, style);

}

Page 33: Effecient javascript

33

Cache property access (Contd.)

BEST

YUD.addClass(this.iframe,"myClass");

Need not change the JavaScript code to change the Look & Feel

Page 34: Effecient javascript

34

Minimize Dom Access

Before if (YUD.get("RetireSMEI") && YUD.get("RetireSMEI").checked)

showEbayForm = 1;

Better: var RetireSMEI=YUD.get("RetireSMEI");

if (RetireSMEI && RetireSMEI.checked) showEbayForm = 1;

Page 35: Effecient javascript

35

Call back functions

• Consider:

var callback = new Function(“// ... “);

foo(somedata, callback);

• Better way to write:

var callback = function () { // ... };

foo(somedata, callback);

• Still better (if you don't need to reuse) callback():

foo(somedata, function () { /// ... });

Page 36: Effecient javascript

36

Call back functions

A common way for one to stumble upon increasing memory problems is to pass an object's method as a callback:

YUE.addListener("recent_select", 'change', this.pushEmail); // sets wrong |this|!

Use:

YUE.addListener("recent_select", 'change', function(e){PAYPAL.love.p2p.pushEmail(e)});

Ref: https://developer.mozilla.org/en/DOM/element.addEventListener

Page 37: Effecient javascript

37

Some YUI tips

1.) YUI addClass, addListener accepts array of input elements

Before

YUE.addListener("expandImg", "click", PAYPAL.Beloved.Salsa.toggleBalanceDetails); YUE.addListener("collapseImg", "click", PAYPAL.Beloved.Salsa.toggleBalanceDetails); YUE.addListener("expandLink", "click", PAYPAL.Beloved.Salsa.toggleBalanceDetails); YUE.addListener("collapseLink", "click", PAYPAL.Beloved.Salsa.toggleBalanceDetails);

Better

YUE.addListener([("expandImg", "collapseImg", "expandLink", "collapseLink“],click,function(e){("collapseLink", PAYPAL.Beloved.Salsa.toggleBalanceDetails(e));

Page 38: Effecient javascript

38

Some YUI tips

2.) getElementsByClassName(className, tagName, rootNode):

Returns an array of elements that have the class name applied. Can be optionally scoped by tagName and/or a root node to increase performance.

Before:-

var pu=YUD.getElementsByClassName('pu');

Better :-

var pu=YUD.getElementsByClassName('pu','input','purchasetabs');

Even better:-

var pu=YUD.get("purchasetabs").getElementsByTagName("input");

Page 39: Effecient javascript

39

Some YUI tips

3.) Use replaceClass instead of removing and adding class

Before:-

YUD.removeClass('goToMyAccount', 'hide');// The above will take more time for removal (through profiling)YUD.addClass('goToMyAccount', 'show');

Better:

YUD.replaceClass('goToMyAccount', 'hide‘,’show’);

//Does a regex replace

Page 40: Effecient javascript

40

Smart Listeners Use Event Delegation when

you have to dump content via Ajax and attach events to the content elements or when u have many similar events that need to be triggered within child elements, or if you have a DHTML heavy page with node created and deleted all the time, this technique is really useful

Event Capture ->Events propagates from Top to Bottom a.k.a. Ancestor to Child

target.addEventListener(type, listener, useCapture); //false by default https://developer.mozilla.org/En/DOM:element.addEventListener

Event Capturing doesn't work in IE. And so in YUI. Because attachevent doesn’t have the third parameter

bSuccess = object.attachEvent(sEvent, fpNotify)

Eg. http://dev.paypal.com/~pkhan/examples/EventCapture.html

Page 41: Effecient javascript

41

Smart Listeners

Event Bubbling -> Events propagates from Bottom to Top a.k.a. Child to Ancestor

Before :-

var lis=getElementsByTagName("li");

YAHOO.util.Event.addListener(lis, "mouseover", callback);

Event Delegation:-

YAHOO.util.Event.addListener("container", "mouseover", callback);

Example: http://dev.paypal.com/~pkhan/examples/EventBubbling.html

http://dev.paypal.com/~pkhan/examples/UsageBubbling.html

Live example:-

https://cms.paypal.com/us/cgi-bin/marketingweb?cmd=_render-content&content_ID=marketing_us/send_money

Page 42: Effecient javascript

42

AJAX

•Ajax ? asynchronous doesn’t mean fast

•Use GET over post

•Use json -> small, native

•Use gzip

Page 43: Effecient javascript

43

Summary• Common subexpression removal

• Use shortcuts wherever possible

• Cache Function Pointers

• Avoid globals

• Avoid try/catch inside loops

• Replace HTML faster than innerHTML

• Minimize DOM objects

• Reduce, Cache DOM Access

• Incorrect this in callback functions

• Add same listener to multiple elements

• Optimize getElementsByClassName

• Use YUI replace Class

• Use event delegation wherever appropriate

• Use GET, Gzip, JSON for Ajax

Page 44: Effecient javascript

44

Questions /Suggestions ???Mail to [email protected]