ECMAScript? Aren't we learning JavaScript?
ECMAScript: The official language name, refers to the standard.
JavaScript: Commonly used name for implementations of ECMAScript standard. Not tied to any version.
ECMAScript 5 (ES5): 5th edition of ECMAScript, what you're used to writing now.
ECMAScript 6 (ES6)/ ECMAScript 2015: 6th edition of ECMAScript, what we're going to be learning.
function fooWithVar (bar) {
if (bar) {
var baz = 'baz';
}
console.log(baz);
}
// to the JavaScript interpreter:
function fooWithVar (bar) {
var baz;
if (bar) {
baz = bar;
}
console.log(baz);
}
fooWithVar('hello world') // hello world
fooWithVar() // undefined
function fooWithLet (bar) {
if (bar) {
let baz = 'baz';
}
console.log(baz);
}
// to the JavaScript interpreter:
function fooWithLet (bar) {
if (bar) {
let baz;
baz = bar;
}
console.log(baz);
}
fooWithLet();
// ReferenceError: baz is not defined
/** What's easier to debug: undefined,
or a ReferenceError? */
'use strict';
const foo = [];
const bar = {};
const baz = 1;
const quux = 'hello';
foo.push(bar); // okay
bar.someMethod = function () {} // also okay
baz = 2 // TypeError: Assignment to constant variable
quux += ' world' // TypeError: Assignment to constant variable
// remember me?
function createFunctions(n) {
let functions = [];
/* because using "let" to declare the variable "i" makes it block-scoped,
you don't have to worry about it being closed over */
for (let i = 0; i < n; i++) {
functions.push(function () {
return i;
});
}
return functions;
}
let myFunctions = createFunctions(5);
myFunctions[0]() // => 0 I work the way you thought I would!
myFunctions[1]() // => 1 Pretty cool, right?
// the combination of let and arbitrary code blocks can help you free up memory faster
function suchCalculation () {
/* very function */
}
/* Arbitrary code blocks are also new with ES6
They prevent scope pollution - no need for a messy IIFE!
*/
{
let muchData = { /* large dataset */ }
suchCalculation(muchData);
}
/* At this point, the garbage collector will have cleaned up the large data set we used,
since we're outside the code block
*/
console.log('wow!');
Remember that, within a function, this is defined based on the execution context of a function
// say we have an object called Neo...
const Neo = {};
// and we also have a function
const bendSpoon = function () {
console.log(this.spoon);
};
// let's give our object a property and a method
Neo.spoon = 'spoon';
Neo.bendSpoon = bendSpoon;
// the execution context of bendSpoon is Neo, so this refers to Neo
Neo.bendSpoon(); // spoon
// the execution context of bendSpoon is the global context - there is no spoon
bendSpoon(); // undefined
const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];
Neo.kick = function (agent) {
console.log('Neo kicked ', agent);
};
Neo.kickAgentSmiths = function () {
agentSmiths.forEach(function (agent) {
this.kick(agent);
});
};
Neo.kickAgentSmiths(); // TypeError: this.kick is not a function
// How can we help Neo?
/* In a single-line arrow function, curly braces are optional,
and the function implicitly returns the value of the last expression.
You can include a return statement if you'd like, but it's optional.
This is called the 'concise body'
*/
arr.map(i => i * 2);
// If you have multiple arguments, they must go in parentheses
arr.reduce((prev, next) => prev + next);
// If you have no arguments, you can just use an empty ()
let foo = () => console.log('bar');
/* If you need more than one line, then you must use brackets
and must include the return statement
This is called the 'block body'
*/
let baz = (x) => {
if (x > 1) return x;
else return 1;
}
const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];
Neo.kick = function (agent) {
console.log('Neo kicked ', agent);
};
Neo.kickAgentSmiths = function () {
agentSmiths.forEach(agent => this.kick(agent));
};
Neo.kickAgentSmiths();
/*
Neo kicked Smith1
Neo kicked Smith2
Neo kicked Smith3
*/
/*
Arrow functions love promise chains and array methods
*/
// Greatest http request of all time
$http.get('/api/artists')
.then(res => res.data)
.then(artists => artists.filter(artist => artist.name !== 'Kanye')
.catch(err => console.error);
const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];
Neo.kick = function (agent) {
console.log('Neo kicked ', agent);
};
// now the kickAgentSmith function's this is no longer Neo
Neo.kickAgentSmiths = () => {
agentSmiths.forEach(agent => this.kick(agent));
};
Neo.kickAgentSmiths(); // TypeError: the Matrix has you
// Call and apply don't affect 'this'
let sum = (a, b) => a + b;
sum.call(null, 1, 2) // 3
sum.apply(null, [1, 2]) // 3
// On that note, don't expect 'arguments' to work
let differenceA = (a, b) => arguments[0] - arguments[1]; // nope
differenceA(2, 1) // TypeError
// See what's happening here?
let returnAnObject = () => { a: 1 };
let obj = returnAnObject();
obj.a; // TypeError: Cannot read property 'a' of undefined
let returnAnObjectForReals = () => ({ a: 1 });
let obj = returnAnObjectForReals();
obj.a; // 1