Skip to content

ES6

Created: 2015-08-25 18:06:19 -0700 Modified: 2021-05-15 11:22:10 -0700

Read this: https://turriate.com/articles/modern-javascript-everything-you-missed-over-10-years

These sites are both great resources:

http://es6-features.org/

http://learnharmony.org/#

I’m only going to be making notes here that I think are useful for the future.

================================================================================

Function with default object parameters

Section titled Function with default object parameters

This syntax gives defaults to not only the operands, but it also gives a default to the object passed in altogether, so you can call it with zero arguments if you want.

function sum({operand1 = 5, operand2 = 6} = {}) {
return operand1 + operand2;
}

Yes, “let” is block-scoped, and will probably become the default scoping for almost everything, because function-scoping (and hoisting) with “var” isn’t usually what you want. However, there’s a nice little gem thanks to having block-scoping:

ES5 (this sucked):

var result = [];
for (var i = 0; i < 5; i++) {
result.push(function () {
return i;
});
}
result[0]() === 5;
result[1]() === 5;

ES6:

var result = [];
for(var i = 0; i < 5; i++) {
**let val = i;**
result.push(function() {
return val;
});
}
console.log('Result[0]: ' + result[0]()); // 0
console.log('Result[1]: ' + result[1]()); // 1
// Slightly cleaner syntax:
let result = [];
for(let i = 0; i < 5; i++) {
result.push(function() {
return i;
});
}
console.log('Result[0]: ' + result[0]()); // 0
console.log('Result[1]: ' + result[1]()); // 1

================================================================================

Shorthand for anonymous functions in JavaScript. Useful for callbacks to turn this:

$("#hi").click(function (event) {
console.log("clicked");
});

Into this:

$("#hi").click((event) => console.log("clicked"));

You can define a whole function with this too:

var addOneAndLog = (x) => {
x++;
console.log("x is: " + x);
return x;
};
var y = addOneAndLog(5); // prints 6
console.log("y is: " + y); // prints 6

To define a function with no arguments, do:

var myFunction = () => {
/_ … body … _/;
};

Context:

  • It still captures any other variables in closures.
  • ‘this’ is always the ‘this’ from the scope that defines the function, meaning call, apply, and bind have NO effect.

Conclusions:

  • Because ‘this’ can’t be modified, I would only ever use this as a lambda or as a quick callback function. You would never use this to write class functions.

================================================================================

A way to destructure objects and arrays:

let test = {name: ‘Adam’, x: 2};

let {name: myName, x: numHands} = test;

console.log(‘My name is: ’ + myName + ’ and I have ’ + numHands + ’ hands.’); // My name is Adam and I have 2 hands.

This is handy for something like this:

function getResult() {

return {

result: 1234,

error: null

};

}

var {result, error} = getResult();

console.log(result); // 1234

I’ve also seen it used in code like this:

ES5: var black = this.props.black;

ES6: const { black } = this.props; // it’s fewer characters and it’s const

You can also do this for arrays:

var [first, last] = [‘Adam’, ‘Learns’];

console.log(first, last); // “Adam” “Learns”

That may be useful if you got data in an array that you know is something it’s in a certain structure, but to me that means you should’ve used an object where you could explicitly name your properties.

However, there is a “car”/“cdr” style too:

let [first, … remaining] = [1,2,3,4,5]; // the ”…” is the “spread” operator

console.log(first); // 1

console.log(remaining); // [2,3,4,5]

You can also implicitly reject elements in an array without having to use something like an “_ignore” variable:

let [,,third] = [1,2,3]; // 3

Similarly, the “spread” operator can be used for calling a function:

var extraParams = [3,4,5];

function foo(a, b, c, d, e) {

console.log(a+b+c+d+e);

}

foo(1,2, … extraParams); // 15 (and note: the spread operator may be copy/pasted out of OneNote as a single-character ellipsis, but it is supposed to be three periods)

The spread operator can also do something like this:

var a = {

foo: 5,

bar: 6

};

var b = {

…a, // shallow-copies all properties of ‘a’ into this, meaning b.foo.nested will now modify a.foo.nested too.

baz: 7

};

console.log(‘b: ’ + JSON.stringify(b)); // b: {“foo”:5,“bar”:6,“baz”:7}

================================================================================

Try to use underscore variables at LEAST in your getters/setters.

Invalid code (well, not invalid, but you’ll infinitely recurse, meaning it won’t work):

class Person {

constructor(first, last) {

this.first = first;

this.last = last;

}

get first() { return this.first; }

set first(f) { this.first = f; } // NOT GOOD: this function calls itself infinitely

toString() {

return ${this.first} ${this.last};

}

}

Change it to:

class Person {

constructor(first, last) {

this.first = first;

this.last = last;

}

get first() { return this._first; } // I only had to change the getter/setter to use “_first” instead of “first”.

set first(f) { this._first = f; } // the rest of the code works fine because these functions don’t need parens to be invoked.

toString() {

return ${this.first} ${this.last};

}

}

Remember: don’t use getters/setters instead of explicit function calls when you want side effects. For example, having a getter for something like “get lengthInInches() { return this.lengthInFeet * 12; }” is fine because you’re not modifying the state of the class. But saying “get nextId() { return this.id++; }” is bad because it’s not obvious that there are even side effects, e.g.:

if (this.nextId > 0 && this.nextId < 10) {

console.log(‘Congrats! You are one of our first users.’);

}

if (this.nextId === 0) {

console.log(‘This will never be hit now!’);

}

The problem is that you’re incrementing nextId just by accessing it, but it’s not obvious to a user of the API that there would even be side effects.

================================================================================

In ES5, you basically had to pollute the global namespace for client-side imports. For example, in the Google Closure Library, you had something like this:

// constants.js

goog.provide(‘my.app.constants’);

my.app.constants.PI = PI = 3.141592653589793;

// main.js

goog.require(‘my.app.constants’);

console.log(my.app.constants.PI); // 3.141592653589793

The problem with this is that you really had a global namespace in the form of “window.my” (well, it wasn’t necessarily rooted at ‘window’, but you get the idea). This meant that “my” was now a global variable.

With ES6, you don’t need to come up with a namespace at all since you import based on relative paths.

import * as constants from “src/constants.js”

constants.PI; // 3.141592653589793

What’s nice is that “constants.js” isn’t the one coming up with the name for the namespace; the user of the file is. This means constants.js can now look like this:

// constants.js

export var PI = 3.141592653589793; // now we don’t care if some other module defined their own PI

================================================================================

This was actually in ES5, but I didn’t know about it:

x = 5;

y = ‘hi’;

console.log(x); // 5

console.log(y); // hi

console.log({x}); // Object {x: 5}

console.log({y}); // Object {y: ‘hi’}

console.log({x, y}); // Object {x: 5, y: ‘hi’}

So console.log({someObj}) is essentially shorthand for console.log(‘someObj: ’ + someObj);

It doesn’t beat the “stringify” snippet I wrote in Sublime to do console.log(‘objectToLogHere: ’ + JSON.stringify(objectToLogHere));

Thanks to default parameters, you shouldn’t ever see something like this anymore:

function Person(name) {

this.name = name || ‘Adam’; // BAD: see below for why

}

Instead, change the signature of the function:

function Person(name=‘Adam’) {

this.name = name; // much nicer!

}

This is great because sometimes defaults didn’t work how you might expect. E.g. if you had a default number that was non-zero:

function Person(numArms) {

this.numArms = numArms || 2; // BAD: if you lost both of your arms and passed in 0, “0 || 2” evaluates to 2, so you’re still given two arms.

}

// Fix it like this:

function Person(numArms = 2) {

this.numArms = numArms;

}

And yes, I actually did run into this in practice. I had something along the lines of “this.resourceMultiplier = multiplier || 1”, so I couldn’t ever set it to 0 thanks to that line.

================================================================================

Note: I am doing this on 8/26/2015, so ES5 is still common enough for me to have to use a transpiler.

mkdir learning_es6

cd learning_es6

express

npm install

npm install —save-dev gulp

npm install —save-dev gulp-babel

npm install —save-dev babel

Make a basic gulpfile.js in the root directory:

var gulp = require(‘gulp’);

var babel = require(‘gulp-babel’);

var paths = {

jsCode: [‘./src/**/*.js’],

dist: ‘dist’

};

gulp.task(‘default’, function () {

return gulp.src(paths.jsCode, {base: ’./’})

.pipe(babel())

.pipe(gulp.dest(paths.dist));

});

Expose “dist” to your views in Express:

app.use(‘/dist’, express.static(path.join(__dirname, ‘dist’)));

Note that I explicitly expose it here as “dist”. If you leave off the first argument, then you can also leave off “dist” from the path below.

Set up your index.jade:

doctype html

html

head

title Learning ES6

script(type=‘text/javascript’, src=‘dist/src/main.js’)

body(onload=‘main.start()’ style=“overflow: hidden;“)

Set up a main.js (as shown above, I put this in a folder called “src”, but you may want it in publicjavascripts):

class Person {

constructor(name) {

this.name = name;

console.log(‘this.name: ’ + JSON.stringify(this.name));

}

}

window.main = {};

main.start = function() {

let person = new Person(‘Adam’);

};

Run “gulp” so that your file is transpiled and placed into the “dist” folder:

gulp

Finally, launch your HTTP server with this on Windows:

set DEBUG=learning_es6:* & node .binwww

Go to http://localhost:3000

Now you’re up and running with ES6/Babel.