ES6 of the Week

This Week's Episode:
symbols, iterators/iterables, and generators, oh my!
Symbols
- a brand new primitive type!
Symbol([description])
var foo = new Symbol("bar")
var foo = Symbol("bar")
The key to my... object?
Uniqueness
Enumerability
Unique symbol butterflies
const slimShady = Symbol('eminem'); // the string 'eminem' is just to help us
const theRealSlimShady = Symbol('eminem');
slimShady === theRealSlimShady // false
theRealSlimShady === theRealSlimShady // true - you have a direct reference
//to the symbol so they point to the same symbol in memory
const fiftyCent = Symbol(); // Symbol is just a unique identifier
typeof fiftyCent === 'symbol'; // a new primitive for real!
let rappers = {};
rappers[fiftyCent] = "Curtis James Jackson III";
// attempting to get using dot notation doesn't work
rappers.fiftyCent // undefined
// you MUST pass in the Symbol reference using bracket notation
rappers[fiftyCent] // "Curtis James Jackson III"
Loop de loop
for... in...
for... of...
Object.keys()
Object.getOwnPropertyNames()
JSON.stringify()
const fooSym = Symbol('foo');
const myObj = {};
myObj['foo'] = 'bar';
myObj[fooSym] = 'baz';
Object.keys(myObj); // -> [ 'foo' ]
Object.getOwnPropertyNames(myObj); // -> [ 'foo' ]
Object.getOwnPropertySymbols(myObj); // -> [ 'foo', Symbol('foo') ]
Pt 2
iterators and iterables
Iterator protocol
- can access items in a collection
- can keep track of its current position
- has a next() method
- returns an object with:
- value
- done
- returns an object with:
Iterable protocol
- defines its iteration behavior
- must have a property with the Symbol.iterator key (either itself or through prototypical inheritance)
Using iterators
'use strict';
// an array is a built-in iterable
let numberArray = [1, 2, 3];
// Symbol.iterator is the special property that contains a function that returns an iterator
let it = numberArray[Symbol.iterator]();
// we can then use the iterator to manually iterate through the array's values
it.next(); // Object { value: 1, done: false }
it.next(); // Object { value: 2, done: false }
it.next(); // Object { value: 3, done: false }
it.next(); // // Object { value: undefined, done: true }
let stringArray = ['a', 'b', 'c'];
// the for...of loop consumes an iterable
for (let ch of stringArray) {
console.log(ch) // a b c
}
DIY
let strArray = ['a', 'b', 'c'];
strArray[Symbol.iterator] = function () {
let idx = 0,
collection = this,
len = this.length;
return {
next: function () {
if (idx < len) {
let value = collection[idx];
idx++;
return {
value: value + '!',
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
};
};
for (let ch of strArray) {
console.log(ch); // a! b! c!
}
Pt 3
generators
Generators
- new type of function that can maintain its own state
- always return an object which adheres to the Iterator Protocol (an iterator)
- uses the yield keyword
Generation station
// we declare a generator using an asterisk
function* myGenerator (n) {
// we yield values with the yield keyword
yield n;
n++;
// we can yield as many times as we want
// each yield is a "pause" in the generator function's execution
yield n;
}
/*
invoking a generator function returns an iterator!
we've passed in a value for the parameter "n", but
no other code in the generator has executed yet
*/
let myIterator = myGenerator(41);
// each call to next advances us to the next yield
let firstYield = myIterator.next();
console.log(firstYield.value); // 41
let secondYield = myIterator.next();
console.log(secondYield.value); //42
Call and Answer
// because we can pause generators, it doesn't matter if they run forever
function* generateDoubles (initialValue) {
let x = initialValue;
while (true) {
x = yield x * 2;
}
/*
Something really cool is happening here!
We can yield on the right side of an assignment.
When we call .next on the iterator, we can pass in a value as a parameter.
That value will then be the value that gets assigned!
*/
}
let i = 1,
it = generateDoubles(1), // invoking the generator 'primes' it with the parameter value
two = it.next().value, // this first call to .next advances us to the yield
four = it.next(two).value, // each subsequent call picks up where the last yield left off!
eight = it.next(four).value;
/*
Each yield pauses the function and yields a value.
If the yield is in an assignment, it will also wait
to receive a value from outside as well!
*/
ES6 of the Week - 5 (symbols, iterators, iterables, and generators)
By beelai88
ES6 of the Week - 5 (symbols, iterators, iterables, and generators)
Symbols, Iterators/iterables, generators
- 613