v8 (Google)
SpiderMonkey (Mozilla)
jsc [javascript core] (Apple)
Chakra (Microsoft) [now open source, yay! \\o//]
+ some less widely known ones
All code will be “modern JS” (ES6, ES2015, whatever you want to call it)
There will be no numbers, I’m sorry.
function MyClass(a) {
return {
getA() { return a; }
};
}
// this creates a fresh function object every time!
const a = new MyClass(1), b = new MyClass(1);
a.getA !== b.getA;
// also, things are *impossible* to debug / inspect
const _a = Symbol("a");
class MyClass() {
constructor(a) {
this[_a] = a;
}
getA() {
return this[_a];
}
}
// method is shared using the prototype chain
// you can still inspect privates in devtools
// BUT! prototype lookup
function asyncAction(payload) {
return (dispatch, getState) => {
setTimeout(() => dispatch({
type: "SOME_ACTION",
payload,
}), getState().delay);
};
}
// This allocates new function objects every time!
function asyncAction(payload) {
return (dispatch, getState) => { // here
setTimeout(() => dispatch({ // and here!
type: "SOME_ACTION",
payload,
}), getState().delay);
};
}
asyncAction(1) !== asyncAction(2);
// nothing we can really do about it,
// in this case at least
fetch("foo")
.then(req => req.json())
.then(json => {
doSomeProcessing(json);
});
// Promises are awesome!
// (apart from being uncancelable, which sucks :-(
const p1 = fetch("foo");
const p2 = p1.then(req => req.json());
const p3 = p2.then(json => {
doSomeProcessing(json);
});
// .then allocates intermediate promise objects
p1 !== p2 && p2 !== p3
const toJSON = req => req.json();
// later inside a function:
fetch("foo")
.then(toJSON)
.then(doSomeProcessing);
// arrow functions are awesome!
// they are lightweight to type <3
// but they have a runtime cost
const Component = ({onClick, children}) => (
<h1 onClick={() => onClick()}>
<span>Oh hi!</span>
{children}
</h1>
);
// "declarative" templates ?
const Component = ({onClick, children}) => {
return React.createElement("h1",
// call, allocates new object
{ // allocates new object
onClick: () => onClick(),
// allocates new function object
},
React.createElement("span", null, "Oh hi!"),
// call, allocates new object
children);
};
// from: https://github.com/maxogden/yo-yo
const list = items => yo`<ul>
${items.map(item => yo`<li>${item}</li>`)}
</ul>`;
// tagged template strings are function calls
// also, template strings are *strings*!!!
// do you really want to parse strings
// *every single time*?
// also that particular library uses *real*
// dom elements to do the diffing *facepalm*
compilers can help, for example by moving constant elements like the `<span>` out of the function, to avoid calling and allocating every time
avoid inline arrow functions, or inline `.bind()`
move binding to the constructor if possible, sadly not for stateless function components :-(
// from https://github.com/paldepind/union-type/
const Action = Type({
Up: [], Right: [], Down: [], Left: []});
const advancePlayer = (action, player) =>
// call, allocates new object
Action.case({
// all of the below: allocate new functions
// captures arguments in closure
Up: () => ({x: player.x, y: player.y - 1}),
Right: () => ({x: player.x + 1, y: player.y}),
Down: () => ({x: player.x, y: player.y + 1}),
Left: () => ({x: player.x - 1, y: player.y}),
_: () => player,
}, action);
// from https://github.com/paldepind/union-type/
// better:
// allocates objects and functions only once!
// can still have some overhead depending on impl
const advancePlayer = Action.caseOn({
Up: (player) => ({x: player.x, y: player.y - 1}),
Right: (player) => ({x: player.x + 1, y: player.y}),
Down: (player) => ({x: player.x, y: player.y + 1}),
Left: (player) => ({x: player.x - 1, y: player.y}),
_: (player) => player
});
// yesterday
for (let i = 0; i < iterable.length; i++) {
const elem = iterable[i];
// ugly to write
}
// or
iterable.forEach(elem => {
// uses closure
// does not work with `NodeList` / `arguments`
});
// today
for (const elem of iterable) {
}
// desugars into, roughly:
const iter = iterable[Symbol.iterator]();
while (true) {
const {elem, done} = iter.next();
if (done) { break; }
}
// awesome!
// works with `NodeList`, `arguments`
// supports custom iterators and generators
// sadly, 3-8x slower than straight for loop
// *sadface*
[1, 2, 3, 4, 5]
.map(x => x * 2)
.filter(x => x % 2)
.reduce((a, x) => a + x, 0);
// whats the runtime cost of this code?
Developer productivity (convenience) is king!
Keep it simple!
Compilers can help a lot, like moving closures and constant react elements out of functions when possible
+ bundling and dead code elimination
Just wanted to raise awareness.
Know what the runtime cost of the code is that you write!
JS (especially asmjs and wasm) is a compiler target:
Bundlers (!!!), jsx, babel, typescript,
C++ and other LLVM languages via emscripten, etc…
Lets embrace it and do expensive things at compile time and not at run time!
Why are JS devs so afraid of doing things at compile time?
Compile times?
# incremental dev builds with hot reloading
webpack built 1e4b27cb4e32b7bb3979 in 264ms
webpack building...
webpack built 992e6902639bbce8f527 in 2012ms
webpack building...
webpack built e113b1cb712bb9eec0f4 in 1147ms
webpack building...
webpack built 2ff58130c885d6d68090 in 2413ms
webpack building...
webpack built 68ebf18365f67cfd64d9 in 1346ms
# production build with uglify
npm run build 2> /dev/null 59,42s user 2,41s system 102% cpu 1:00,16 total
+ another rant…
import/export modules is the best thing that happened, because they are statically analyzable
but node core developers want to force `*.mjs` on us :-(
// lib/foo.js
export default (a, b) => a + b;
// module.js
import add from "lib/foo.js"
add(1, 2);
TypeScript and Flow becoming more popular!
Can avoid real problems/bugs (undefined not a function), …
Can optimize code based on type guarantees…
// like rewriting
for (const elem of array) {
}
// into
for (let i = 0; i < array.length; i++) {
const elem = array[i];
}
Why isn’t anyone doing it?
// a compiler plugin!
// it kind of is due to jsx, but it does not go far enough!
// transform this:
const FnComponent = props => (
<h1>{props.title}</h1>
);
// into something similar to this:
class FnComponent {
constructor() {
this.elem = document.createElement('h1');
this.text = document.createTextNode();
this.elem.appendChild(this.text);
}
render(props) {
this.text.data = props.title;
}
// …
}
// not creating / comparing useless js objects all the time
// just updates the data that’s needed
// generate different code for client or SSR *at compile time*!