July 17th, 2015
// highway to callback hell
var that = this;
d3.getData(function(x){
d3.getMoreData(x, function(y){
d3.getEvenMoreData(y, function(z){
that.process(x, y, z);
});
});
});
// what is this, Java!?
EnterpriseGrade.prototype.getStrategyCounters = function(arr) {
// what is this, C!?
for (var i = 0, getNums = []; i < arr.length; i++) {
getNums[i] = function() {
return arr[i]; // hope this works...
};
}
}
System.import('main.js');
define(['foo'], function (foo) {
var baz = require('baz');
return function () { foo.doSomething(baz); };
});
improvements to ECMA-262
Block Scoping
Arrow Functions
Better Object Literals
Destructuring
Template Strings
Function Parameter Improvements
Symbols
Iterators
Generators
Classes
Modules
Comprehensions
Asynchronous Functions
Decorators
New Operators
Babel.js
Subsequent JavaScript editions are
Strict Supersets
of their predecessors
Block scoping (ES6)
const a = 1;
let b = 2;
if (a) {
let b = 1;
console.log(b);
let x = 1;
}
console.log(x) // not defined
a = 2; // static error (immutable value)
let b = 1; // static error (re-declaration)
"use strict";
var a = 1;
var b = 2;
if (a) {
var _b = 1;
console.log(_b);
var _x = 1;
}
console.log(x);
Block Scoping (ES6)
no more reason to use var
!
for (let = ...)
const
provides immutable referencesBetter Object Literals (ES6)
let obj = {
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ "prop_" + (() => 42)() ]: 42
};
"use strict";
var _obj;
var _get = function get(_x, _x2, _x3) {...
function _defineProperty(obj, key, value) {...
var obj = _obj = _defineProperty({
// Shorthand for ‘handler: handler’
handler: handler,
// Methods
toString: function toString() {
// Super calls
return "d " + _get(
Object.getPrototypeOf(_obj),
"toString",
this
).call(this);
}
}, "prop_" + (function () {
return 42;
})(), 42);
// Computed (dynamic) property names
Better Object Literals (ES6)
{prop}
{method() { super.method() }}
Arrow Functions (ES6)
let bob = {
_name: "Bob",
nameGetter() {
return f => this._name;
}
}
let f = x => x + 1;
let fn = (x, y, z) => { return x + y + z; };
let noop = ()=>{};
let curry = x => y => x + y;
// won't work! must wrap object in parens...
let literal = x => {a : 1};
var bob = {
_name: "Bob",
nameGetter: function nameGetter() {
var _this = this;
return function (f) {
return _this._name;
};
}
};
var f = function f(x) {
return x + 1;
};
var fn = function fn(x, y, z) {
return x + y + z;
};
var noop = function noop() {};
var curry = function curry(x) {
return function (y) {
return x + y;
};
};
Arrow Functions (ES6)
lexical this!
var this = _this)
parens + braces matter depending on the context
Destructuring (ES6)
let arr = [1, 2, 3, 4, 5];
let w = {
x : 1,
y : { z : [2, 3] }
};
// unpack arrays, with defaults
let [a=2, b, ...rest] = arr;
// unpack object properties, at any level
let {x} = w;
// get fancy
let {
y : {
z: [p, q]
},
y
} = w;
"use strict";
var _slicedToArray = (function () { ...
var arr = [1, 2, 3, 4, 5];
var w = {
x: 1,
y: { z: [2, 3] }
};
// unpack arrays, with defaults
var _arr$0 = arr[0];
var a = _arr$0 === undefined ? 2 : _arr$0;
var b = arr[1];
var rest = arr.slice(2);
// unpack object properties, at any level
var x = w.x;
// get fancy
var _w$y$z = _slicedToArray(w.y.z, 2);
var p = _w$y$z[0];
var q = _w$y$z[1];
var y = w.y;
Destructuring (ES6)
var prop = obj.prop, x = obj.p[0] .....
let {prop, p :[x, y]} = obj;
Much DRY-er extraction of variables
extends to function parameters!
Template Strings (ES6)
app.directive('Test', function() {
return {
template : `
<div class="{{'classy'}}">
Text
</div>
`
}
})
let imputed = `http://${domain}/?${params}`
let v1 = 1, v2 = 2;
// no parens!
console.log `a${v1}b${v2}c`
// => ["a","b","c"] 1 2
'use strict';
function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
app.directive('Test', function () {
return {
template: '\n <div class="{{\'classy\'}}">\n Text\n </div>\n '
};
});
var imputed = 'http://' + domain + '/?' + params;
var v1 = 1,
v2 = 2;
console.log(
_taggedTemplateLiteral(
['a', 'b', 'c'], ['a', 'b', 'c']
), v1, v2
);
// => ["a","b","c"] 1 2
Template Strings (ES6)
Multiline strings including whitespace!
Nested quotes (of multiple types)
Easy inline HTML
Variable Interpolation
String.raw
function f(a=true, ...rest) {
return a ? rest.join(", ") : rest;
}
let g = ({x, y}) => {
x += 1; z += 1;
return {x, y};
}
let h = (a, b, c) => a + b + c;
let a = [1, 2, 3];
console.log( h(...a) )
"use strict";
function f() {
var a = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
rest[_key - 1] = arguments[_key];
}
return a ? rest.join(", ") : rest;
}
function g(_ref) {
var x = _ref.x;
var y = _ref.y;
x += 1;z += 1;
return { x: x, y: y };
}
var h = function h(a, b, c) {
return a + b + c;
}
var a = [1, 2, 3];
console.log(h.apply(undefined, a));
Function Parameter Improvements (ES6)
...args > Array.prototype.slice.call(arguments)
arr.push(...args) > arr.push.apply(arr, args)
Default parameters
Symbols (ES6)
var MyClass = (function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
return this[key];
}
};
return MyClass;
})();
// new symbol
var key = Symbol("key");
var c = new MyClass("hello")
console.log(c[key] === undefined)
"use strict";
var MyClass = (function () {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function doStuff() {
return this[key];
}
};
return MyClass;
})();
// new symbol
var key = Symbol("key");
var c = new MyClass("hello");
console.log(c[key] === undefined);
Symbols (ES6)
(Essentially) Private methods / attributes, useful for collision prevention
Iterators (ES6)
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
"use strict";
function _defineProperty(obj, key, value) {...
var fibonacci = _defineProperty(
{},
Symbol.iterator,
function () {
var pre = 0,
cur = 1;
return {
next: function next() {
var _ref = [cur, pre + cur];
pre = _ref[0];
cur = _ref[1];
return { done: false, value: cur };
}
};
}
);
Iterators (ES6)
Iterators are any object which has a method next
which is defined as...
Objects with a [Symbol.iterator
] method are iterable (Array, Set, Map, arguments, String
)
Iterators (continued) (ES6)
for (let n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = fibonacci[Symbol.iterator](), _step;
!(_iteratorNormalCompletion = (_step = _iterator.next()).done);
_iteratorNormalCompletion = true) {
var n = _step.value;
// truncate the sequence at 1000
if (n > 1000) break;
console.log(n);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"]) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
Iterators (ES6)
for (let item of iterable)...
Generators (ES6)
function* naturalNumbers() {
let n = 1;
while (true) yield n++;
}
for (let n of naturalNumbers()) {
if (n > 20) {
break;
}
console.log(n);
}
var marked0$0 = [naturalNumbers].map(regeneratorRuntime.mark);
function naturalNumbers() {
var n;
return regeneratorRuntime.wrap(function naturalNumbers$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
n = 1;
case 1:
if (!true) {
context$1$0.next = 6;
break;
}
context$1$0.next = 4;
return n++;
case 4:
context$1$0.next = 1;
break;
case 6:
case "end":
return context$1$0.stop();
}
}, marked0$0[0], this);
}
Generators (ES6)
Generators return Iterables
Babel slices the function context between the yield statements and produces an FSM
Classes (ES6)
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = (
SkinnedMesh.defaultMat()
);
}
update(camera) {
super.update();
}
static defaultMat() {
return new THREE.Matrix4();
}
}
var _createClass = (function () {...
var _get = function get(_x, _x2, _x3) {...
function _classCallCheck(instance, Constructor) {...
function _inherits(subClass, superClass) {...
var SkinnedMesh = (function (_THREE$Mesh) {
_inherits(SkinnedMesh, _THREE$Mesh);
function SkinnedMesh(geometry, materials) {
_classCallCheck(this, SkinnedMesh);
_get(Object.getPrototypeOf(SkinnedMesh.prototype), "constructor", this).call(this, geometry, materials);
this.idMatrix = SkinnedMesh.defaultMat();
}
_createClass(SkinnedMesh, [{
key: "update",
value: function update(camera) {
_get(Object.getPrototypeOf(SkinnedMesh.prototype), "update", this).call(this);
}
}], [{
key: "defaultMat",
value: function defaultMat() {
return new THREE.Matrix4();
}
}]);
return SkinnedMesh;
})(THREE.Mesh);
Classes (ES6)
Classes are just sugar (concise and convenient sugar)
Modules (ES6)
import _ from 'lodash';
import {a, b, c} from './mycode';
_.map(a, b);
export const one = 1;
export default function main() {}
exports.__esModule = true;
var _temporalUndefined = {};
var one = _temporalUndefined;
exports['default'] = main;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _temporalAssertDefined(val, name, undef) { if (val === undef) { throw new ReferenceError(name + ' is not defined - temporal dead zone'); } return true; }
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _mycode = require('./mycode');
_lodash2['default'].map(_mycode.a, _mycode.b);
exports.one = one = 1;
exports.one = _temporalAssertDefined(one, 'one', _temporalUndefined) && one;
function main() {}
Modules (ES6)
Named / default exports
Comprehensions (array) (ES7)
let evens = [
for (n of [1,2,3,4,5,6])
if (n%2 === 0)
n
]
"use strict";
var evens = (function () {
var _evens = [];
var _arr = [1, 2, 3, 4, 5, 6];
for (var _i = 0; _i < _arr.length; _i++) {
var n = _arr[_i];
if (n % 2 === 0) {
_evens.push(n);
}
}
return _evens;
})();
Comprehensions (generator) (ES7)
let evens = ( // <- note parens
for (n of [1,2,3,4,5,6])
if (n%2 === 0)
n
)
var _this = this;
var evens = regeneratorRuntime.mark(function callee$0$0() {
var _arr, _i, n;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
_arr = [1, 2, 3, 4, 5, 6];
_i = 0;
case 2:
if (!(_i < _arr.length)) {
context$1$0.next = 10;
break;
}
n = _arr[_i];
if (!(n % 2 === 0)) {
context$1$0.next = 7;
break;
}
context$1$0.next = 7;
return n;
case 7:
_i++;
context$1$0.next = 2;
break;
case 10:
case "end":
return context$1$0.stop();
}
}, callee$0$0, _this);
})();
Comprehensions (ES7)
only for ... of
and if
are allowed
(...) -> generator, [...] -> array
need double parens if doing a generator comprehension in a function call -> fn(( for (c of ...) ... ))
let evens = (
for (n of [1,2,3,4,5,6])
for (j of [1,2,3])
for (z of [5,6,7])
if (j > 6)
n
)
Async Functions (ES7)
async function get() {
let val = await Promise.resolve(1);
console.log(val);
return val;
}
get().then(v => console.log(v + 1));
let getter = async() => { /*...*/ }
"use strict";
function get() {
var val;
return regeneratorRuntime.async(function get$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
context$1$0.next = 2;
return regeneratorRuntime.awrap(Promise.resolve(1));
case 2:
val = context$1$0.sent;
console.log(val);
return context$1$0.abrupt("return", val);
case 5:
case "end":
return context$1$0.stop();
}
}, null, this);
}
get().then(function (v) {
return console.log(v + 1);
});
Async Functions (ES7)
Async functions return Promises
Babel executes them via a Iterator FSM, calling next
on resolution of await
statements
await* [...] === await Promise.all([....])
Try / Catch works like synchronous code
Decorators (ES7)
@annotationFactory(true)
@nameThis
class MyClass { }
let nameThis = target => {
target.named = 'target';
};
let annotationFactory = value => target => {
target.annotated = value;
};
'use strict';
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var MyClass = (function () {
function MyClass() {
_classCallCheck(this, _MyClass);
}
var _MyClass = MyClass;
MyClass = nameThis(MyClass) || MyClass;
MyClass = annotationFactory(true)(MyClass) || MyClass;
return MyClass;
})();
var nameThis = function nameThis(target) {
return target.named = 'target';
};
var annotationFactory = function annotationFactory(value) {
return function (target) {
target.annotated = value;
};
};
Decorators (ES7)
Simply functions which wrap a class upon definition
::console.log === console.log.bind(console)
obj::method()::other() === other.call(method.call(object))
a**b === Math.pow(a, b)
babel-eslint : Use Babel for linting of new syntax