Post on 14-Jan-2017
transcript
Front End WorkshopsII. (Not so) Basic JavaScript
concepts
Mario García Martínmgarcia@visual-engin.com
Good practices in JavaScript“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you
live.”— John Woods
Avoid global variables
// homer.jsvar isDrunk = true;
// bart.jsvar isDrunk = false;
// index.html<script src=”./homer.js”></script><script src=”./bart.js”></script>
console.log(isDrunk);
Global variables and functions can be overwritten by other scripts.
Declare variables with var
Using ‘use strict’ directive will prevent undeclared variable definitions.
function defineBeer() {beer = 1;
}defineBeer();
Not doing so results in global variable declaration.
'use strict';function defineBeer() {
beer = 1;}defineBeer();
Uncaught ReferenceError: beer is not defined
function defineBeer() {var beer = 1;
}defineBeer();
Manipulating the DOM
$('#homer).addClass('alcoholic');// Some logic here...$('#homer').addClass('angry');
Writing the DOM
Reading the DOMvar $homer = $('#homer');$homer.addClass('alcoholic');// Some logic here...$homer.addClass('angry');
var $ul = $('#target-ul');for (var i=0; i<4; i++) {
$ul.append('<li>' + i + '</li>');}
var html = '';for (var i=0; i<4; i++) {
html += '<li>' + i + '</li>';}$('#target-ul').append(html);
Avoid heavy nestingfunction logHomerBehaviour(homer) {
if (homer.isAtWork()) { for (var i=0; i<4; i++) { if (homer.isAsleep()) {
if (homer.isSnoring()) {for (var j=0; j<2; j++) {
snore += 'Zz...'; }
console.log(snore);}
} else {console.log('Doughnuts!');
}}
}}
function logHomerBehaviour(homer) { if (!homer.isAtWork()) { return; }
for (var i=0; i<4; i++) {if (homer.isAsleep()) { logHomerSleeping(homer);} else { console.log('Doughnuts!');}
}}
function logHomerSleeping(homer) { if (!homer.isSnoring()) { return; }
console.log('Zz...Zz...');}
Comment your code“Good code explains itself”.
function handleEvent(ev) {// In IE9 and earlier, use the window.event.ev = ev || window.event;}
Comment what you consider needed, but don’t tell others your life story.
Comment your code“Good code explains itself”.
function handleEvent(ev) {// In IE9 and earlier, use the window.event.ev = ev || window.event;}
Comment what you consider needed, but don’t tell others your life story.
Other good practices
Avoid eval functionNever pass a string to setTimeout or setInterval
Use === instead of ==console.log(0 == false); // trueconsole.log('2' == 2); // true
console.log(0 === false); // falseconsole.log('2' === 2); // false
Tools: JS Lint
The only valid measurement of code quality...
WTFs/minute
More information in...● http://www.slideshare.net/cheilmann/javascript-best-practices-1
041724
● https://www.youtube.com/watch?v=hQVTIJBZook
● https://www.devbridge.com/articles/javascript-best-practices/
● http://www.codeproject.com/Articles/580165/JavaScript-Best-Practices
(Ir)Regular expressions“Some people, when confronted with a problem, think ‘I know, I’ll use regular expressions.’ Now they have two
problems.”— Jamie Zawinski
Creating a regular expressionvar regexp1 = new RegExp('abc', 'gi');var regexp2 = /abc/gi;
/abc/ A sequence of characters/[abc]/ Any character from a set of characters/[^abc]/ Any character not in a set of characters/[0-9]/ Any character in a range of characters/x+/ One or more occurrences of pattern x/x*/ Zero or more occurrences/x?/ Zero or one occurrence/x{2,4}/ Between two and four occurrences
/(abc)/ A group/a|b|c/ Any one of several patterns/\d/ Any digit character/\w/ An alphanumeric character [a-zA-Z0-9_]/\s/ Any whitespace character/./ Any character except newlines/^/ Start of input/$/ End of input
Using a regular expression in JavaScript
/[0-9]/.test('in 1992'); //true
var neighbor = /neighbou?r/;neighbor.test('neighbour'); // trueneighbor.test('neighbor'); // true
Through the RexExp object
Through the String object'12345'.match(/(\d)(\d)+/); // ['12345', '1', '5']'Homer drinks beer'.search(/beer/); // 13'Ho+me[]r'.replace(/[^\w\s]/g, ''); // 'Homer''Homer drinks beer'.split(/\s/); // ['Homer', 'drinks', 'beer']
var match = /\d+/.exec('one two 100');console.log(match); // ['100']console.log(match.index); // 8
Do not abuse regular expressions...
^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|
30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?
[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\
d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579]
[26]00|[2468][048]00)|(?:20)?(?:0[48]|[2468][048]|[13579][26]))$
More information in...● Mastering Regular Expressions, by Jeffrey E.F. Friedl● Introducing Regular Expressions, by Michael Fitzgerald● Regular Expressions Cookbook, by Jan Goyvaerts and Steven
Levithan● https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Re
gular_Expressions● http://eloquentjavascript.net/09_regexp.html● http://www.cheatography.com/davechild/cheat-sheets/regular-
expressions/
Scopes and Closures. Prototype“First learn computer science and all the theory. Next develop a programming style. Then forget all that and
just hack.”— George Carrette
Lexical scopeIn JavaScript, scopes are declared by functions, not by blocks.
// Global scope
if (true) {var x
= 24;}
console.log(x);
var x;if (true) {
x = 24;}
console.log(x);
Hoisting
Lexical scopevar hero = aHero();
var newSaga = function() {
var foil = aFoil();
var saga = function() {
var deed = aDeed();
console.log(hero + deed + foil);
}
}
Execution context (in-memory scope)
var hero = randStr();var newSaga = function() { var foil = randStr(); var saga = function() { var deed = randStr(); console.log(hero + deed + foil);
}saga();saga();}newSaga();newSaga();
hero = ‘gal’newSaga = [ Function ]
foil = ‘cow’saga = [ Function ]
deed = ‘eyes’
foil = ‘cow’saga = [ Function ]
Execution context (in-memory scope)
var hero = randStr();var newSaga = function() { var foil = randStr(); var saga = function() { var deed = randStr(); console.log(hero + deed + foil);
}saga();saga();}newSaga();newSaga();
hero = ‘gal’newSaga = [ Function ]
foil = ‘cow’saga = [ Function ]foil = ‘cow’saga = [ Function ]
deed = ‘tips’
Execution context (in-memory scope)
var hero = randStr();var newSaga = function() { var foil = randStr(); var saga = function() { var deed = randStr(); console.log(hero + deed + foil);
}saga();saga();}newSaga();newSaga();
hero = ‘gal’newSaga = [ Function ]
foil = ‘cow’saga = [ Function ]foil = ‘cat’saga = [ Function ]
deed = ‘Rubs’
Execution context (in-memory scope)
var hero = randStr();var newSaga = function() { var foil = randStr(); var saga = function() { var deed = randStr(); console.log(hero + deed + foil);
}saga();saga();}newSaga();newSaga();
hero = ‘gal’newSaga = [ Function ]
foil = ‘cow’saga = [ Function ]foil = ‘cat’saga = [ Function ]
deed = ‘Robs’
‘this’ keywordInvocation as a method
var homer = { beer: 'Nice to have another one!', info: function() { console.log(this === homer); console.log(this.beer); }};homer.info(); // true, 'Nice to have another one!'
var bart = { beer: 'Too young'};bart.info = homer.info;bart.info(); // false, 'Too young'
function bar() { console.log(this);}bar(); // global
‘this’ keywordInvocation as a method var anum = 0;
var foo = { anum: 10, baz: { anum: 20, bar: function() { console.log(this.anum); } }}foo.baz.bar(); // 20var hello = foo.baz.bar;hello(); // 0
var foo = { baz: function() { console.log(this); }}foo.baz(); // foo
var anotherBaz = foo.baz;anotherBaz(); // global
‘this’ keywordInvocation as a constructor
function Person() {this.x = 0;
}
var person = new Person();console.log(person.x); // 0
‘this’ keywordInvocation with the apply and call methodsfunction juggle() {
var result = 0;for (var n = 0; n < arguments.length; n++) {result += arguments[n];
}this.result = result;
}var ninja1 = {};var ninja2 = {};juggle.apply(ninja1, [1, 2, 3, 4]); // ninja1.result = 10;juggle.call(ninja2, 5, 6, 7, 8); // ninja2.result = 26;
ClosuresCombines two things: a function, and the environment in which that function was created.
var toast = 'Cheers!';function makeToast() {
console.log(toast);}
makeToast();function
makeToast() { ... }
var toast
Closuresvar scope = 'global scope';function checkscope() {
var scope = 'local scope';
function f() { return scope; }
return f();}checkscope(); // 'local scope';
var scope = 'global scope';function checkscope() {
var scope = 'local scope';
function f() { return scope; }
return f;}var func = checkscope();func(); // 'local scope';
Closuresvar fns = [];for (var i=0; i<4; i++) {
fns.push(function() {console.log(i);
});}
fns[0](); // 4fns[1](); // 4fns[2](); // 4fns[3](); // 4
var fns = [];for (var i=0; i<4; i++) { fns.push( (function(a) {
return function() { console.log(a);};
})(i) );}fns[0](); // 0fns[1](); // 1fns[2](); // 2fns[3](); // 3
Constructor functionfunction Animal(_name) {
var name = _name;
this.getName = function() {console.log('My name is ' + name);
};this.setName = function(_name) {
name = _name;};
}var animal = new Animal('Santa');animal.getName();// My name is Santaanimal.setName('New Santa');animal.getName();// My name is New Santa
Prototype chainvar o = { a: 1 };
// o --> Object.prototype --> null
var a = ['Homer', 'Marge', 'Lisa'];
// a --> Array.prototype --> Object.prototype -->
null
function f() { return 2; }
// f --> Function.prototype --> Object.prototype -->
null
Prototype chainfunction Animal(_name) { // Instance properties can be set on each instance of the class this.name = _name;}
// Prototype properties are shared across all instances of the class.Animal.prototype.getName = function() { console.log('My name is ' + this.name);};
var animal = new Animal('Santa');animal.getName(); // My name is Santa
Inheritancefunction Cat(_name) { Animal.call(this, _name);}
Cat.prototype = new Animal();Cat.prototype.scratch = function() {
console.log('I love to scratch!');}
var cat = new Cat('Claws');cat.getName(); // My name is Clawscat.scratch(); // I love to scratch!
More information in...● Secrets of the JavaScript Ninja, by John Resig● Javascript, the definitive guide, by David Flanagan● Scope & closures, by Kyle Simpson● http://www.sitepoint.com/demystifying-javascript-variable-scop
e-hoisting/● http://davidshariff.com/blog/what-is-the-execution-context-in-j
avascript/● http://davidshariff.com/blog/javascript-scope-chain-and-closure
s/● http://davidshariff.com/blog/javascript-this-keyword/
Memory leaks“‘Yeah, it works but you’re leaking memory everywhere. Perhaps we should fix that”. I’ll just restart apache every
10 requests.”— Rasmus Lerdorf
Circular references
<div id="target">Element</div>
<script type="text/javascript">var obj = document.getElementById('target');
document.getElementById('target').property = obj;obj.bigString = new Array(10000).join('*');
</script>
Example 1
Circular references
<div id="target"></div>
<script type="text/javascript">function myFunction(element) {
this.elementReference = element;element.property = this;
}function leak() {
new myFunction(document.getElementById('target'));
}leak();
</script>
Example 2
Closures and circular references<button id="element">Click Me</button>
<script type="text/javascript">function outerFunction() {
var obj = document.getElementById('element');obj.onclick = function innerFunction() {
console.log('Hi! I will leak');};obj.bigString = new Array(10000).join('*');
};outerFunction();
</script>
Closures. Break the circular reference<button id="element">Click Me</button>
<script type="text/javascript">function outerFunction() {
var obj = document.getElementById('element');obj.onclick = function innerFunction() {
console.log('Hi! I have avoided the leak');};obj.bigString = new Array(10000).join('*'));obj = null; //This breaks the circular reference
};outerFunction();
</script>
Closures. Add another closure<button id="element">Click Me</button>
<script type="text/javascript">function outerFunction() {
var anotherObj = function innerFunction() {console.log('Hi! I have avoided the leak');
}; (function anotherInnerFunction() {
var obj = document.getElementById('element');obj.onclick = anotherObj;
})();};
outerFunction();</script>
Closures. Avoid the initial closure<button id="element">Click Me</button>
<script type="text/javascript">function outerFunction() {
var obj = document.getElementById('element');obj.onclick = doesNotLeak;
}function doesNotLeak() {
console.log('Hi! I have avoided the leak');}
outerFunction();</script>
More information in...● https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memo
ry_Management● http://www.ibm.com/developerworks/web/library/wa-memleak/● https://msdn.microsoft.com/en-us/magazine/ff728624.aspx
Thanks for your attention!Leave your questions on the comments section