Effecient javascript

44
1 Writing Efficient JavaScript Code Prem Nawaz Khan

description

Writing Efecient

Transcript of Effecient javascript

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]