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 from way back when?
function arrayOfFuncs(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 = arrayOfFuncs(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 know it can clean up the large data set we used,
since we're outside the code block
*/
console.log('wow!');
// 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?
/* if you have one argument and don't use brackets
you can just write a snappy one-liner
This is called the 'concise body'
*/
arr.map(i => i * 2);
// multiple arguments go in parentheses
arr.reduce((prev, next) => prev + next);
// no arguments are just an empty ()
// note that you can use this for any function expression, not just callbacks!
let foo = () => console.log('bar');
/* if you need more than one line, then you need to use brackets
and can't omit 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
*/
const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];
Neo.kick = function (agent) {
console.log('Neo kicked ', agent);
};
Neo.kickAgentSmiths = () => {
agentSmiths.forEach(agent => this.kick(agent));
};
Neo.kickAgentSmiths(); // TypeError: the Matrix has you
/**
* Arrow functions love promise chains and array methods
*/
// Angular!
$http.get('/api/artists')
.then(res => res.data)
.then(artists => artists.filter(artist => artist.name === 'Kanye')
.catch(err => console.error);
// Express!
router.get('/', (req, res, next) => {
Artist
.findAll({})
.then(artists => res.json(artists)
.catch(next);
});
'use strict';
function Dog (name, breed) {
this.name = name;
this.breed = breed;
}
// instance methods
Dog.prototype.bark = function () {
console.log('arf!');
};
Dog.prototype.sayName = function () {
console.log('Hi, my name is ', this.name);
};
// static methods
Dog.fetchAll = function () {
return $http.get('/api/dogs')
.then(res => res.data)
.catch(console.error);
};
const ben = new Dog('Ben', 'pitbull');
ben.bark(); // arf!
class Dog {
constructor (name, breed) {
this.name = name;
this.breed = breed;
}
sayName () {
console.log('Hi, my name is ', this.name);
}
bark () {
console.log('arf!');
}
static fetchAll () {
return $http.get('/api/dogs')
.then(res => res.data)
.catch(console.error);
}
}
const ben = new Dog('Ben', 'pitbull');
ben.bark() // arf!
'use strict';
function Animal (name) {
this.name = name;
}
Animal.prototype.sayName = function () {
console.log('Hi, my name is ', this.name);
};
function Dog (name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
console.log('arf!');
};
'use strict';
class Animal {
constructor (name) {
this.name = name;
}
sayName () {
console.log('Hi, my name is ', this.name);
}
}
class Dog extends Animal { // the 'extends' keyword extends the parent's prototype
constructor (name, breed) {
super(name); // the 'super' keyword replaces 'Animal.call(this, name)'
this.breed = breed;
}
bark () {
console.log('arf!');
}
}
'use strict';
class Dog {
constructor (name, breed) {
this.name = name;
this.breed = breed;
}
}
// totally fine!
Dog.prototype.sayName = function () { /** etc */ }
console.log(typeof Dog) // "function"