On Github themouette / slides-js101
By Julien Muetton / @themouette
OpenStudio 2013 December, 5th
When altering a node, DOM is rendered again.
To avoid performances issues, you need to work on a detached piece of DOM.
A Fragment is an empty DOM node.
automatic semicolon insertion force to put braces at the EOL
// BAD return { foo: "foo" };
vs
// GOOD return { foo: "foo" };
Use `Array.join()` to declare multiline strings
// BAD var foo = 'Lorem ipsum dolor sit amet, consetetur ' + 'sadipscing elitr, sed diam nonumy ';
vs
// GOOD var foo = [ 'Lorem ipsum dolor sit amet, consetetur ', 'sadipscing elitr, sed diam nonumy ' ].join('');
Lambda functions are shown as (anonymous) in debugger.
// BAD var foo = function() { /* ... */ };
vs
// GOOD var foo = function foo() { /* ... */ };
Variable initialization should remain readable
// BAD var foo = new Object(); foo.name = 'John'; foo.age = 32;
vs
// GOOD var foo = { name: 'john', age: 32 };
Semicolons `;` are optional as ASI insert them for us.
a = b + c foo()
// All right a = b + c ; foo()
a = b + c [1].push(a)
// Not what expected a = b + c[1].push(a)
a = b + c (opts || {}).foo ? bar() : baz()
// Not what expected a = b + c(opts || {}).foo ? bar() : baz()
Either you learn ASI rules or you write semicolons
To go further on ASI: DailyJS on ASI
More advices in the Airbnb JavaScript Style Guide
var foo = "outside"; function test() { var foo = "inside"; console.log('test foo is "%s"', foo); } test(); console.log('outside foo is "%s"', foo);Run
Will output
test foo is "inside" outside foo is "outside"
Each function defines its own context and can access definition context.
Any variable not declared with var is global.
var foo = "outside"; function test() { foo = "inside"; console.log('test foo is "%s"', foo); } test(); console.log('outside foo is "%s"', foo);Run
Will output
test foo is "inside" outside foo is "inside"
var foo = 1; function bar() { if (!foo) { var foo = 10; } alert(foo); } bar(); Run
// Is compiled into var foo = 1; function bar() { var foo; if (!foo) { foo = 10; } alert(foo); } bar();
Variable declarations are hoisted to the top of context by compiler
var a = 1; function b() { a = 10; return; function a() {}; } b(); alert(a); Run
// Is compiled into var a = 1; function b() { // declare a symbol locally function a() {}; a = 10; return; } b(); alert(a);
Function declarations are hoisted too.
To go further on hoisting: adequatelygood
A closure is a function alongside a referencing environment.
To be short, you can reference variables that belongs to the context where function was defined.
In the meantime, a function creates its own context.
var x = 0; function incr() { x++; console.log(x); } function decr() { x--; console.log(x); } console.log(x); // log 0 incr(); // log 1 incr(); // log 2 decr(); // log 1Run
Create a callback from a variable:
function createAdd(number) { function doAdd(value) { return value + number; } return doAdd; } var add10 = createAdd(10); alert(add10(1)); // returns 11Run
Closure creates it's own context:
var x = 5; function foo() { var x = 12; function myFun() { // do something } return myFun; } console.log(foo()); // log function myFun() {...} console.log(x); // log 5 console.log(myFun); // reference errorRun
var john = { name: "John" }; john.sayHello = function () { alert("Hello " + this.name); }; john.sayHello();Run
this is the object.
var name = "Garry"; function sayHello() { alert("Hello " + this.name); } sayHello();Run
this is the current context:
var name = "Garry"; var joe = { name: "Joe" }; joe.sayHello = function sayHello() { alert("Hello " + this.name); } var fct = joe.sayHello; fct();Run
var joe = { name: "Joe" }; function sayHello(greeting, to) { greeting || (greeting = 'Hello'); to || (to = 'annonymous'); alert([greeting, to, 'I\'m', this.name].join(' ')); } sayHello.call(joe, 'Good morning', 'Garry'); sayHello.apply(joe, ['Good morning', 'Garry']);Run
will both result into
Good morning Garry, I'm Joe.
function bind(context, method) { return function () { method.apply(context, arguments); } }
This way, you can make sure the context is what you expect.
In jQuery it is called $.proxy.
Read more on partial application on Cowboy's blog and Invocation patterns in depth
A constructor is a simple function
var User = function User(properties) { // init properties if falsy properties || (properties = {}); this.firstname = properties['firstname']; this.lastname = properties['lastname']; };
Instantiation is as expected:
var john = new User({ firstname: "John", lastname: "Doe" });
User.prototype.sayHello = function sayHello() { alert("Hello, I'm " + this.firstname); };
To be more efficient, use extend function:
$.extend(User.prototype, { sayHello: function sayHello() { alert("Hello, I'm " + this.firstname); }, goodBye: function goodBye() { alert("Bye !"); } });
Never override native objects prototypes.
Use IIFE to isolate context
var myModule = (function (window, undefined) { var privateVariable; function foo () { } function bar () { } return { foo: foo, bar: bar }; })(window);
More about IIFE on Cowboy's blog
Asynchronous Module Definition relies on define.
define([ 'jquery', 'app/model/user' ], function ($, User) { var user = new User(); /* ... */ return something; });
curljs and requirejs are amd loader.
amdclean removes extra define
browserify allow to use nodejs dependency model.
module.exports = (function () { var User = require(__dirname + '/model/user'); var u = new User(); /* ... */ return something; })();
jQuery is none of CommonJS Promize compliant but is consistent.
$.ajax( "/example" ) // when request is successful .done(function(response) { alert("success"); }) // Error happened .fail(function(xhr, statusText, error) { alert("error"); }) // executed in both cases .always(function() { alert("complete"); });
Note there is a single callback to handle all returned data.
$.when( $.ajax("/example"), $.ajax("/sample") ) // called when both succeed .done(function (example, sample) { alert ('both returned'); }) // called when any request fails .fail(function () { /* ... */ });
$.ajax( "/example" ) .fail(function () { alert('example failed') }) .pipe( function success(example) { return $.ajax('/sample'); }, function error() { alert('example failed !') }) // From here, this is about second request .done(function (sample) { alert ('sample returned'); }) .fail(function () { alert('sample failed') });
Easy way to tweak inconsistant apis.
$.ajax( "/example" ) .then( function success(example) { // filter data return {response: example}; }, function error() { /*...*/ }, function progress() { /*...*/ } ) // `data` has been altered by `then` callback .done(function (data) { console.log(data.response); });