babel src/foo.js > dist/foo.jsfunction Something(...) { ... }
Something.prototype =
Object.create(Super)
Something.prototype.foo = function() { ... }
Something.prototype.bar = function() { ... }
This isn't great
class Something extends Super {
constructor(...) { ... }
foo() { ... }
bar() { ... }
}
I'd say this is better
class Person {
get age() {
return this._age - 5
}
set age(val) {
this._age = val + 5
}
}
class Date {
static now() { ... }
}
console.log(Date.now())
function Class(a, b) {
Super.call(this, a, b)
}
Class.prototype.method = function() {
if (something)
Super.prototype.method.call(this)
}
This is not super
class Class extends Super {
constructor(a, b) {
super(a, b)
}
method() {
if (something) super.method()
}
}
This is okay
var MyClass = mixin(class extends Super {
...
})
Class expressions
obj = {
method1(x) { return x + 1},
method2(y) { return y - 1}
}
obj = {x, y}
// is equivalent to
obj = {x: x, y: y}
obj = {__proto__: null}
// is equivalent to
obj = Object.create(null)
propName = "onclick"
obj = {
foo: 10,
[propName](e) { e.preventDefault() }
}
let x = 5
{
let x = 10
// local x is 10
}
// our old x is still 5
const x = 5 x = 6 // x is still 5
Constants
if (condition) {
console.log(localFunc())
function localFunc() { return "hi" }
}
// localFunc does not exist here
Hoisted local functions
for (let i = 0; i < a.length; i++) console.log(a[i])
New scope for each iteration
f = function(x) { return x + 1 }
18 characters of boilerplate
f = x => x + 1
2 characters of boilerplate
f = x <= x + 1
while (x --> 0) { ... }
Not to be confused with
f = () => 1 f = (x) => x + 1 f = (x, y) => x + y
array.filter(x => x < 100).map(x => x * .01)
setTimeout(() => alert("BOO"), 500)
n.addEventListener("click", e => this.click(e))
More one-liners!
this.click??array.forEach(elt => {
let elt2 = elt * elt
if (elt2 > 100)
console.log(elt, elt2)
})
Braces create a body
array.map(elt => {
x: elt,
y: 2,
z: true
})
Braces don't create an object!
array.map(elt => ({
x: elt,
y: 2,
z: true
}))
Wrap them in parentheses
let [x, y] = [1, 2]
Array patterns
let {x: x, y: y} = {x: 1, y: 2}
// equivalent to
let {x, y} = {x: 1, y: 2}
Object patterns
let {body: {lastChild: {nodeName, nodeType}}}
= document
Nest as deep as you like
let {x = 0, y = 0} = {x: 1}
Defaults
let [x, y] = [1, 2] const [x, y] = [1, 2] var [x, y] = [1, 2]
In any binding construct
[x, y] = [1, 2];
({x, y} = {x: 1, y: 2})
Direct assignment
function length({x, y}) {
return Math.sqrt(x * x + y * y)
}
In function arguments
function indexOf(arr, elt, start=0) {
...
}
Argument default values
let {x: {y}} = {z: 10}
// BOOM Cannot read property 'y' of undefined
Destructuring can fail
let x = [1, 2] let y = [...x, 10, ...x] // y is [1, 2, 10, 1, 2]
No more .concat().concat().concat()
let [a, b, ...rest] = [1, 2, 3, 4] // rest is [3, 4]
Use it in patterns too!
function printf(format, ...args) {
...
}
Get a (real) array of arguments
let nums = [4, 10, 2] console.log(Math.max(5, ...nums))
More convenient than .apply
`I am a string`
(Really, it is)
`these are\nthree lines`
(Escapes and newlines!)
`Hello ${name}`
(Interpolation?!)
String.raw`^\w\S*` // → "^\\w\\S*"
Prevent escape interpretation
foo`one ${two} and ${three}`
// is equivalent to
foo(["one ", " and ", ""], two, three)
let names = new Map names.set(Array, "the array constructor") names.set(Math, "the math object") names.get(Array) // → "the array constructor"
Re-vo-lutionary (?)
map.size map.set(key, value) map.get(key) map.delete(key) map.has(key) map.clear()
set.size set.add(key) set.delete(key) set.has(key) set.clear()
jQuery.get("/data").then(function(text) {
return jQuery.get("/user/" + text)
}).then(function(text) {
console.log(text)
})
function file(path) {
return new Promise((succeed, fail) => {
fs.readFile(path, "utf8", (error, content) => {
if (error) fail(error)
else succeed(content)
})
})
}
// Player A
Array.prototype.search =
function(element, start, end) { ... }
// Player B
Array.prototype.search =
function(predicate) { ... }
Not going to end well
const search = Symbol("search")
Array.prototype[search] =
function(element, start, end) { ... }
array[search]("x", 2, 6)
class Everything {
static [Symbol.hasInstance](obj) {
return true
}
}
iterator.next() // → {value, done}
Iterator interface
class Iterable {
[Symbol.iterator]() { return ... }
}
Iterable interface
for (let elt of array) {
...
}
For/of loops over an iterable
for (let char of string) {
...
}
Strings are also iterable
for (let [key, value] of map) {
...
}
As are Map and Set
[... "foo"] // → ["f", "o", "o"]
Spread operator accepts any iterable
function forEach(array, f) {
for (let i = 0; i < array.length; i++)
f(array[i])
}
Take an internal iterator
function* forEach(array) {
for (let i = 0; i < array.length; i++)
yield array[i]
}
Turn it into an external one
function* integers(from, to) {
for (let i = from; i <= to; i++) yield i
}
for (let i of integers(100, 200))
console.log(i)
Didn't for/of take an iterable?
function Iterator {}
Iterator.prototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
[][Symbol.iterator]()))
class Tree {
constructor(value, left, right) { ... }
*[Symbol.iterator]() {
if (this.left) yield* this.left
yield this.value
if (this.right) yield* this.right
}
}
function countDown(n) {
return {
next(add = 0) {
n += add - 1
if (n < 0) return {done: true}
else return {value: n, done: false}
}
}
}
Resettable countdown
function* countDown(n) {
while (n >= 0) n += (yield --n) || 0
}
Resettable countdown
function* getUser(name) {
let data = JSON.parse(yield get("/u/" + name))
if (data.avatar)
data.pic = yield get("/i/" + user.avatar)
return data
}
drive(function*() {
let user = yield* getUser(USERNAME)
let friends = []
for (let friend of user.friends)
friends.push(yield* getUser(friend))
renderUser(user, friends)
})
Render the user
function drive(generator) {
let iterator = generator()
function resume(result) {
if (result.done) return
result.value.then(
value => resume(iterator.next(value)),
error => resume(iterator.throw(error)))
}
resume(iterator.next())
}
export let pi = 3
export function area(r) { return pi * r * r }
export class Point { ... }
Exporting is easy
export default 42
Default export
import {pi, area} from "./math"
Importing is also easy
import fourtyTwo from "./42"
Importing the default export
import {pi as three} from "./math"
console.log(three)
You can rename imports
import * as math from "./math" console.log(math.pi)
Or wrap them in an object
export {pi as three, area} from "./math"
Re-export
export {pi, area}
Export after the fact
System.import("./math").then(mod => ...)
Dynamic run-time import (?)
let bytes = new Uint8Array(1024) bytes.fill(16, 512, 1024) console.log(bytes[0], bytes[512]) // → 0 16
Compact typed arrays
Array.from(arrayLike) Array.of(1, 2, 3) array.copyWithin(0, 2, 4) array.fill(1, 1, 3) find(x => x > 10) findIndex(x => x > 10)
More array methods
isNaN(undefined) // → true Number.isNaN(undefined) // → false
Cleaned-up number functions
parseInt("011") // → 9
Number.parseInt("011") // → 11
Cleaned-up number functions
/[😀-😚]/.test("😂") // → ERROR
/[\ud83d\ude00-\ud83d\ude1a]/.test("😂") // → false
/[😀-😚]/u.test("😂") // → true
RegExp.unicode
let s = String.fromCodePoint(0x1f602) // → "😂" s.length // → 2 s.charAt(0) // → 55357 s.codePointAt(0) // → 128514
"x".repeat(10) // → "xxxxxxxxxx"
Advanced stuff