JavaScript 101

By Julien Muetton / @themouette

OpenStudio 2013 December, 5th

HTML inclusion

  • Always include scripts at the bottom as it stops page execution
  • CSS in the header to avoid multiple rendering
  • Limit the number of API requests as network is slow
  • Concat and compress your assets for all previous reasons

Document Object Modeler

Document fragments

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.

CS: Braces

automatic semicolon insertion force to put braces at the EOL

// BAD
return
{
    foo: "foo"
};

vs

// GOOD
return {
    foo: "foo"
}; 

CS: Strings

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(''); 

CS: name your functions

Lambda functions are shown as (anonymous) in debugger.

// BAD
var foo = function() {
    /* ... */
};

vs

// GOOD
var foo = function foo() {
    /* ... */
};

CS: use JSON

Variable initialization should remain readable

// BAD
var foo = new Object();
foo.name = 'John';
foo.age = 32;

vs

// GOOD
var foo = {
    name: 'john',
    age: 32
};

CS: Semicolons

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

Contexts

var foo = "outside";
function test() {
    var foo = "inside";
    console.log('test foo is "%s"', foo);
}

test();
console.log('outside foo is "%s"', foo);

Will output

test foo is "inside"
outside foo is "outside"

Each function defines its own context and can access definition context.

Local vs Global

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);

Will output

test foo is "inside"
outside foo is "inside"

Hoisting: variables

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();
// 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

Hoisting: functions

var a = 1;
function b() {
    a = 10;
    return;
    function a() {};
}

b();
alert(a);
// 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

Definition

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.

Example 1: public API

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 1

Example 2

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 11

Example 3

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 error

Invocation pattern

aka this keyword

When calling a method on an object

var john = {
    name: "John"
};

john.sayHello = function () {
    alert("Hello " + this.name);
};

john.sayHello();

this is the object.

When Calling A Function

var name = "Garry";

function sayHello() {
    alert("Hello " + this.name);
}

sayHello();

this is the current context:

Common Gotcha

var name = "Garry";
var joe = {
    name: "Joe"
};

joe.sayHello = function sayHello() {
    alert("Hello " + this.name);
}

var fct = joe.sayHello;

fct();

Force execution context

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']);

will both result into

Good morning Garry, I'm Joe.

Context binding

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

Object Model

Thank you !

Questions ?