Transpilation is complicated
Well, yes, but...
So is updating Javascript
Lots of moving parts
Disruptive to workflow
WTF
Turns out, languages are hard.
ES3 brought...
ES5 brought...
ES6 will bring...
Fat Arrows!
Destructuring!
Rest Parameters!
Spread Operators!
Default Parameters!
Block-level bindings!
Template Strings!
Generators!
Classes!
Proxies!
Maps! Sets!
Promises!
Symbols!
Function names!
New Static Methods!
Proper Tail Call Optimization!
(glad you asked)
Some things we just can't make happen in ES5 alone
But we can get pretty close...
is even scoped to plain blocks
let
const foo = "I AM CONSTANT AS THE NORTHERN STAR";
foo = "NO UR NOT"; // Will not compile -- "foo" is read-only
const foo = {
foo: 'bar'
};
foo.foo = 'baz'; // This is valid
foo = { foo: 'baz' }; // This is not
Consts are pretty nifty, too
Shorthand defaults are simple, but dangerous!
Default parameters are good!
if (someBusinessLogic(data)) {
return performSomeAction(data);
} else if (someOtherBusinessLogic(data, metadata)) {
return performSomeAction(data, metadata);
}
let args;
if (someBusinessLogic(data)) {
args = [data];
} else if (someOtherBusinessLogic(data, metadata)) {
args = [data, metadata];
}
return performSomeAction(...args);
mise-en-place programming
Simplify one-line methods
Even use them with multi-line methods
.bind(this)
.bind(this)
.bind(this)
Now all sexy and clean-looking
{
inheritedFunction: function() {
someAsynchronousTask().then(function() {
this.inherited(arguments);
}.bind(this));
}
}
{
inheritedFunction: function() {
var _args = arguments;
someAsynchronousTask().then(function() {
this.inherited(_args);
}.bind(this));
}
}
{
inheritedFunction: function() {
someAsynchronousTask().then(() => this.inherited(arguments));
}
}
Just suck values right out of an array
You can even do it with objects
WHY NOT STRINGS?
Defaults
Parameters
Object Shorthand
Expressions can also be substituted
Tagged Template Literals are crazy, but very interesting
var obj = {};
obj.foo = 'foo';
obj.bar = 'bar';
'bar' in obj; // true
for (let k in obj) {
console.log(obj[k], k);
}
// foo foo
// bar bar
delete obj.bar;
var map = new Map();
map.set('foo','foo');
map.set('bar','bar');
map.has('bar'); // true
map.forEach((value, key) =>
console.log(key, value));
// foo foo
// bar bar
map.delete('bar');
map.clear();
map.set('foo', 'foo')
.set('bar', 'bar')
.set('baz', 'baz')
.set('quux', 'quux');
let arbitrary_object = { lorem: 'ipsum' };
map.set(arbitrary_object, 'holy cow!');
map.size; // 5
var list = [];
list.push(1);
list.push(2);
list.push(3);
list.push(2);
list.length === 4; // true
for (let value of list) {
console.log(value);
}
// 1
// 2
// 3
// 2
var set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(2);
set.size === 3; // true
set.forEach(value => console.log(value));
// 1
// 2
// 3
set.delete(1);
set.clear();
set.add(1)
.add(2)
.add(3);
let arbitrary_object = { lorem: 'ipsum' };
set.add(arbitrary_object);
set.has(1); // true
Maps and Sets are great
BUT
WHAT ABOUT MEMORY?
let set = new WeakSet();
set.add(arbitrarily_large_object); // The object is loaded into WeakSet
// just like Set, but now can still
// be garbage collected
set.add(3); // TypeError! WeakMaps & WeakSets *MUST* use Objects.
set.size; // Nonexistent!
set.has(arbitrarily_large_object); // true
let map = new WeakMap();
map.set(arbitrarily_large_object, any_other_value);
map.set(3, any_other_value); // TypeError!
map.size; // Nonexistent!
map.has(arbitrarily_large_object); // true
function range(min, max, callback) {
for (let i = min; i <= max; i++) {
callback(i);
}
}
range(2, 3, (i) => console.log(`calling for ${i}: `))
// calling for 2
// calling for 3
function* range(min, max) {
for (let i = min; i <= max; i++) {
yield i;
}
}
for (let i of range(2, 3)) {
console.log(`calling for ${i}`);
}
// calling for 2
// calling for 3
for-of
lets you iterate over values of an iterable
function* range(min, max) {
for (let i = min; i <= max; i++) {
yield i;
}
}
let generator = range(2,3);
/**
* generator ~= {
* next: function() { [native code] },
* throw: function() { [native code] },
* return: function() { [native code] }
* }
*/
let result = generator.next();
/**
* result = {
* value: 2,
* done: false
* }
*/
result = generator.next();
/**
* result = {
* value: 3,
* done: false
* }
*/
result = generator.next();
/**
* result = {
* done: true
* }
*/
can also inject values into a function
yield
Want to iterate over your objects?
Spread works great with any Iterable
Promise.all
Promise.race
Good question.
let original = {};
let proxy = new Proxy(original, {
get: function(original, key, proxy) {
console.log(`looking for ${key} in the original object`);
if (key in original) {
return original[key];
} else {
return original[key] = 42;
}
},
set: function(original, key, value) {
console.log(`trying to set ${key} to ${value}`);
if (key in original) {
return original[key] = value;
} else {
throw new TypeError('Cannot set a nonexistent property through this proxy');
}
}
});
proxy.foo; // 42
proxy.foo = 30; // 30
try {
proxy.bar = 'FAIL'; // TypeError: Cannot set a nonexistent property through this proxy
} catch (e) {
console.log('broke because', e.message);
}
proxy.bar; // 42
proxy.bar = 'WIN'; // 'WIN'
BabelJS REPL: https://goo.gl/Tv9Ebr
import Foo from "aFooModule";
import Bar from "aBarModule";
import * as ChangedName from "anotherModule";
import { map, zip } from "utils/lodash";
// ... do something with these dependencies
export default {
myNewModule,
otherItems
};
export class MyClass {
// ...
}
export function relatedToMyClass() { ... }
let foo = 'bar';
export foo;
Bound Native Methods on Github