JavaScript: The Good, the Bad and the New Parts


Jordi Boggiano   @seldaek

http://nelm.io/

About Me

Belgian living here in Zürich

Weby stuff for 10 years
 http://seld.be

OSS user & contributor
 http://github.com/Seldaek

Recently co-founded Nelmio
We do Symfony2 & Frontend Performance consulting
 http://nelm.io

The Good Parts

The Good Parts

Subset of the JavaScript language

There are things you may want and things that you may need. Wanting something is not a good reason to use it if it adds ambiguity or could potentially introduce bugs. (via @mikeal)

i++;
i += 1;
            
while (i < 50) {
    doStuff(someStuff[i], i * 5, moreStuff[i]);
    doMoreStuff(moreStuff[i++]);
}
while (i < 50) {
    doStuff(someStuff[i], i * 5, moreStuff[i]);
    doMoreStuff(moreStuff[i]);
    i += 1;
}
            
i++;i++;
i += 2;
            

Subset of the JavaScript language

There are things you may want and things that you may need. Wanting something is not a good reason to use it if it adds ambiguity or could potentially introduce bugs. (via @mikeal)

alert(0 == false);
alert('' == false);
alert([] == false);
            
alert("frontendconf" == false);
alert("frontendconf" == true);
            

The above converts booleans to int first then "frontendconf" to int according to the ECMAScript coercion rules.

if ("frontendconf") {
    alert("frontendconf" === "frontendconf");
}
            

Guidance

Proper Consistent whitespace, enforce strict mode

Restrictions

No ++, no ==, no eval, force strict, etc.

Incarnated by JSLint

http://www.jslint.com/

The "Good"* Parts

*Interestingly dangerous

FUN.apply()

Max value of an array

var numbers = [3, 5, 10, 2, 3];
alert(Math.max.apply(null, numbers));
alert(Math.max(numbers[0], numbers[1], numbers[2], numbers[3], numbers[4]));
            

Concat arrays

var a = [1,2,3];
var b = ['a','b','c'];
Array.prototype.push.apply(a, b);
alert(a);
            

Repeat Repeat Repeat

Array literals really are the way to go, yet..

alert(new Array(5+1).join('five '));
            

Named function expressions

var factorial = function (n) {
    if (!n || n == 1) {
        return 1;
    }
    return n * factorial(n-1);
};
alert(factorial(3));
            
var factorial = function f(n) {
    if (!n || n == 1) {
        return 1;
    }
    return n * f(n-1);
};
alert(factorial(3));
alert(f);
            
var zomg = function n() {
    alert(n);
};
zomg();
alert(n);
            

Named function expressions

Self-referencing functions, without arguments.callee - which is deprecated

Guards (&&) and Defaults (||)

More than logical operators

Default values

var f = function (a) {
    a = a || 2;
    alert(a);
}
f(1);
f(false);
f();
            
alert('' && ('' || 'c'));
            
alert(null && (false || 'c'));
            
alert('a' && (false || 'c'));
            
alert('a' && ('' || 'c') && 'd' );
            
alert('a' && ('' || 'b') && false);
            

Labels

continue 2; in JS

var i, j;
for (i = 0; i < 3; i++) {
    for (j = 0; j < 3; j++) {
        continue;
        alert('foo');
    }
    alert('bar');
}
alert('done');
            
var i, j;
main:for (i = 0; i < 3; i++) {
    sub:for (j = 0; j < 3; j++) {
        continue main;
        alert('foo');
    }
    alert('bar');
}
alert('done');
            

Labels, the other side of the coin

Goto in JS

var something = true;
do {
    alert('a');
    if (something) {
        break;
    }
    alert('b');
} while (false);
alert('c');
            
var something = true;
foo: {
    alert('a');
    if (something) {
        break foo;
    }
    alert('b');
}
alert('c');
            

The Bad Parts

Maximized Confusion

alert(Math.min() < Math.max());
            
alert(Math.min());
alert(Math.max());
            

Variable hoisting

var test = 'foo';
function foo() {
    alert(test);
    test = 'bar';
    alert(test);
}
foo();
            
var test = 'foo';
function foo() {
    alert(test);
    var test = 'bar';
    alert(test);
}
foo();
            

Variable hoisting

var test = 'foo';
function foo() {
    alert(test);
    var test = 'bar';
    alert(test);
}
foo();
            

What JS sees:

var test = 'foo';
function foo() {
    var test;
    alert(test);
    test = 'bar';
    alert(test);
}
foo();
            

The lying "floor operator"

alert(~~27.5);
alert(Math.floor(~~27.5));
            
var month = 9, year = 2011;
var jdn = function(y, m, d) {
  var tmp = (month <= 2 ? -1 : 0);
  return ~~((1461 * (y + 4800 + tmp)) / 4) +
         ~~((367 * (m - 2 - 12 * tmp)) / 12) -
         ~~((3 * ((year + 4900 + tmp) / 100)) / 4) +
         d - 2483620;
};
alert(jdn(2011, 12, 24));
            
alert(~~'FOO');
alert(Math.floor('FOO'));

alert(~~(-27.5));
alert(Math.floor(-27.5));
            

JIT endeavors

Keep types stable

function selectElements(selector) {
    selector = document.querySelectorAll(selector);
    return selector;
}

function selectElementsRealQuick(selector) {
    var elems;
    elems = document.querySelectorAll(selector);
    return elems;
}

alert(selectElements('.slide'));
alert(selectElementsRealQuick('.slide'));
            

JIT endeavors

Keep types stable: Same object shapes

var f = function (obj) {
    return obj.a;
}
f({a: 'b'});
f({a: 'c', foo: 'bar'});
            

JIT endeavors

Keep dense arrays

0..N (not N..0) or push

var arr = [], arr2 = [], i;
for (i = 0; i < 5; ++i) {
    arr[i] = 'val';
}
arr2.push('val');
arr2.push('val');
arr2.push('val');
arr2.push('val');
arr2.push('val');
            

JIT endeavors

Avoid inline functions (many functions, all unique)

var asyncProgramming = function() {
    doStuff(function() {
        moar(function() {
            yayNesting(function() {
                areWeThereYet(function() {
                    alert(42);
                });
            });
        });
    });
};
            

ETAGO

End-tag open delimiter

<script>
    document.write("<script src='lala'></script>");
</script>
            
<script>
<!--
    document.write("<script src='lala'></script>");
-->
</script>
<script>
    document.write("<scr"+"ipt src='lala'></sc"+"ript>");
</script>
            
<script>
    document.write("<script src='lala'><\/script>");
</script>
            

The New Parts

Strict Mode - a DOCTYPE for JS

"use strict";
            
var f = function () {
    "use strict";
};
            

Strict Mode - a DOCTYPE for JS

var sloppy  = function () {
    eval("var x = false;");
    alert(x);
    y = 'foo';
    with (window) {
        alert(y);
    }
};
sloppy();
            
var strict = function () {
    "use strict";
    eval("var x = false;");
    alert(x);
    y = 'foo';

    with (window) {
        alert(y);
    }
};
strict();
            

Strict Mode - a DOCTYPE for JS

Other benefits

Security, debugging, performance, minimizability, showing off

Support: FF4+, Chrome13+, Safari5.1+, IE10+ (likely)

ECMAScript 6 / Harmony

Upcoming version of the ES spec

ES6: Let, fixing your block scopes

for (var i = 0; i < 3; i++) {
    setTimeout(function () {
        alert(i);
    }, 1);
}
alert(typeof i);
            
for (var i = 0; i < 3; i++) {
    (function (i) {
        setTimeout(function () {
            alert(i);
        }, 1);
    })(i);
}
alert(typeof i);
            
Execute
for (let i = 0; i < 3; i++) {
    let j = i;
    setTimeout(function () {
        alert(j);
    }, 1);
}
alert(i);
            

ES6: Let, fixing your block scopes

Throwaway variables

Execute
let x = 1;
let y = 2;

let ( x = 5, y = 5 * y ) {
    $.alert(x);
    $.alert(y);
}
$.alert(x);
$.alert(y);
            

ES6: Non-strict pattern-matching (Destructuring)

Non strict matching

var x, y;

[x, y] = ['a', 'b', 'c'];

alert(x);
alert(y);
            

Var swap

var a = 1,
    b = 2;
alert(a + " / "+ b);

[a, b] = [b, a];
alert(a + " / "+ b);
            

Destructuring objects

var { a: x, b: y } = { a: 1, b: 2 };
alert(x + " / " + y);
            

ES6: Non-strict pattern-matching (Destructuring)

Optional named arguments

var foo = function(opts) {
    alert(opts.foo);
    alert(opts.bar);
}

foo({ foo: 'FOO', bar: 'BAR' });
foo({ bar: 'BAR', foo:'FOO'});
            
var foo = function({ foo: name, bar: project}) {
    alert(name);
    alert(project);
}

foo({ foo: 'FOO', bar: 'BAR' });
foo({ bar: 'BAR', foo:'FOO'});
            

ES6: Non-strict pattern-matching (Destructuring)

Shorthand notation

var {a, b, c} = {a: 10, b: 20, c: 30};
alert(a);
alert(b);
alert(c);
            

ES6: Rest & Spread

var f = function (a, b) {
    var rest = [].slice.call(arguments, 2);
    alert(rest);
}
f(1, 2, 3, 4);
            
var f = function (a, b, ...rest) {
    alert(rest);
}
f(1, 2, 3, 4);
            
var f = function (a, b, c) {
    alert(a + b + c);
}
var data = [1, 2, 3];
f.apply(null, data);
            
var f = function (a, b, c) {
    alert(a + b + c);
}
var data = [1, 2, 3];
f(...data);
            

ES6: More

Modules

Generators

Proxies

Tons of syntax-sugar, maybe.



Try some of it with:

<script type="application/javascript;version=1.7">
    // ...
</script>
            

Or use http://code.google.com/p/traceur-compiler/

JSZürich Meetups

Monthly events, @jszurich

http://techup.ch/

References & Links

Thank you.

Slides up at slides.seld.be

JSMOFO T-Shirt
http://nelm.io/shop/

jordi@nelm.io