Skip to content

Instantly share code, notes, and snippets.

@AloofBuddha
Created October 7, 2016 19:07
Show Gist options
  • Save AloofBuddha/7bb23890e25249a6ed110ee681c2e567 to your computer and use it in GitHub Desktop.
Save AloofBuddha/7bb23890e25249a6ed110ee681c2e567 to your computer and use it in GitHub Desktop.
ES6 tutorial
// ES6 of the week: arrow functions and lexical scope
// https://github.com/lukehoban/es6features
// arrow functions are a shorthand for function syntax,
// which make them a lot more succint for functions that take functions
// (i.e. 'higher order functions')
var evens = [2,4,6,8,10];
// traditional function
var odds = evens.map(function(val) {
return val + 1;
});
// 'expression body' syntax, automaticall returns
var odds3 = evens.map(val => val + 1);
// with multiple arguments and 'statement body' syntax
var byIndex = even.map((val, index) => {
return { index: index, val: val};
});
/* LEXICAL THIS!!! */
var traditional = {
name: "Ben",
siblings: ["Lauren", "Ben"],
printFriends: function () {
this.siblings.forEach(function(sibling) {
// this.name will not refer to "Ben" because
// this is rebound in the new function passed to forEach
console.log(this.name, "<3", sibling)
});
}
}
var oldWay = {
name: "Ben",
siblings: ["Lauren", "Ben"],
printFriends: function () {
var that = this;
this.siblings.forEach(function(sibling) {
// this will work because we bound this to that
// when it had the relevant value
console.log(that.name, "<3", sibling)
});
}
}
var es6way = {
name: "Ben",
siblings: ["Lauren", "Ben"],
printFriends: function () {
this.siblings.forEach(sibling => {
// this will work because arrow functions
// do not rebind this, so it still has the context
// of its parent function
console.log(this.name, "<3", sibling)
});
}
}
// HOISTING, LET and VAR
// hoisting can give you unexpected, silent errors
function implicitHoisting(x) {
if (x) {
var y = 'hello'
}
console.log(y + ' world!');
}
implicitHoisting(true); // outputs 'hello world!'
implicitHoisting(false); // no faliure, outputs 'undefined world!'
// Why?
// in Javascript, var is function scoped,
// meaning a declared variable exists for the entire scope of
// its containing function.
// It also has a behavior called 'hoisting', where all variable
// declerations are 'hoisted' to the top of the function
// so the compiler actually sees hoistingExample as
function explicitHoisting(x) {
var y; // new variables equal undefined by default
if (x) {
y = 'hello'
}
console.log(y + ' world!');
}
explicitHoisting(true); // outputs 'hello world!'
explicitHoisting(false); // no faliure, outputs 'undefined world!'
// Now this behavior makes sense
// So how does 'let' help us?
// Let is 'lexically scoped', which means variables only exist
// within their containing braces
// function noHoistingWithLet(x) {
// if (x) {
// let y = 'hello'
// }
// console.log(y + ' world!');
// }
// Trying to *create* this function throws
// 'ReferenceError: y is not defined' - that's good, I think!
// It is called 'lexical scoping' because we can know the scope
// of a variable lexically, i.e. just my reading the file.
// ---------------------------
// Remeber this?
function arrayOfFuncs(n) {
var functions = [];
for (var i = 0; i < n; i++) {
functions.push(function () {
return i;
});
}
return functions;
}
var myFunctions = arrayOfFuncs(3);
myFunctions.forEach(function(fn) {
console.log(fn());
});
// outputs: 3 3 3
// Why? Let's think about closures for a second
function arrayOfFuncsExplicit(n) {
var functions;
var i;
functions = [];
for (i = 0; i < n; i++) {
functions.push(function () {
return i;
});
}
return functions;
}
// our pushed function references a variable
// i, but it isn't contained in it's scope. However,
// because of closures, it still can reference this varaible
// however this variable is shared for all of the functions!
// therefore, at the end of the containing function i = 3, and
// when out functions are called they will all be referencing this i
function arrayOfFuncsLet(n) {
let functions = [];
for (let i = 0; i < n; i++) {
functions.push(() => i);
}
return functions;
}
// what's different here? each i assignment only
// exists for the life of it's for loop iteration
// this means the i being referenced int he function closure
// is a *different* i for each. So it keep the value we would THINK it would have!
// In general, let should replace var, always!
// ----------
// CONST
const x = 1;
// const is like let, but can only be assigned once, at declaration
// x = 2; // This is an error!
// be wary though, const only enforces reassignment, it still
// allows mutation
const arr = [1,2,3];
// arr = arr.map(x => x * 2); // not allowed!
for (let i = 0; i < arr.length; i++) {
arr[i] *= 2;
}
console.log(arr); // [2, 4, 6]
// DEFAULTS
// old way
function foo (a, b) {
b = b || 'bar'; // if b has a value, it equals that, if undefined, it equals 'bar'
return a + b;
}
// ES6 way
function foo2 (a, b='bar') {
return a + b;
}
// It's as simple as that!
// REST
// old way
function printAllArgs() {
var args = Array.prototype.slice.call(arguments);
args.forEach(function (arg) {
console.log(arg)
})
}
// ES6 way
function printAllArgs(...args) {
args.forEach(arg => console.log(arg));
}
// ... is the rest operator. When put in front of a variable inside a parameter
// it means 'collect all unbound comma seperated arguments into an array'
function printAllArgs(first, second, ...rest) {
console.log(first)
console.log(second)
console.log(rest)
}
printAllArgs(1) // 1 undefined []
printAllArgs(1, 2) // 1 2 []
printAllArgs(1, 2, 3, 4) // 1 2 [3, 4]
// SPREAD
// spread is like the inverse of rest - it takes an array and 'spreads' it
// out into a bunch of comma seperated values
function add3(x, y, z) {
return x + y + z;
}
let nums = [1, 2, 3];
add3(...nums); // same as foo.apply(foo, arr);
// spread can be used very effectively for copying arrays
let arr1 = [1,2,3];
let arr2 = [...arr1];
// or pushing a lot of elements to an array
let arr3 = [4,5,6];
arr3.push(...arr1);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment