(with cool names)

HIDDEN GEMS

Boolean isOdd(int num) {
  if(num == 2147483647) {
    return true;
  } else if (num == 0) {
    return false;
  }
  return isOdd(num+=2);
}

Know your language

ES6

Hidden gems from ES6

+

ES

+

90'S STYLE!

Hidden gems from ES6...

FRESH SYMBOL

The

of bel air

smoothMove.js

TM

export default makeSmooth(element) {
  // magically make js

  // do animation wow

  // much awesome

}

Let's mark the element as 'moving'

  1. Will appear in every iteration
    (for-of or Object.keys)
  2. Can collide with existing libraries
  3. Can collide with future libraries
  4. Can collide with DOM standards
export default makeSmooth(element) {
  if(!element.isMoving) {
    // do much magic wow
  }
  element.isMoving = true;
}

A New Primitive

1. undefined

3. BOOLEAN

4. number

5. string

6. object

2. null

SYMBOL

A New Primitive

SYMBOL

const a = Symbol();
const b = Symbol();

a == b // false
const a = Symbol('name');

console.log(a) // 'Symbol(name)'

SYMBOL

log.levels = {
    DEBUG: Symbol('debug'),
    INFO: Symbol('info'),
    WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

a symbol can be used as a unique value

SYMBOL

const isMoving = Symbol("isMoving");
...
if (element[isMoving]) {
  // awesome smooth stuff
}
element[isMoving] = true;

a symbol can be used as AN Object key

1. guaranteed not to collide

2. NON-ENUMERABLE

3. are NOT STRINGIFIED

const isMoving = Symbol("isMoving");
element[isMoving] = true;

element.isMoving = false;
element['isMoving'] = false;
element.<everythingPossible> = false;

element[isMoving] = true;
const cool = Symbol("Cool");
myObj[cool] = true;

Object.keys(myObj) // []

for(let key of myObj) {
  console.log(key) // nothing
}
const secret = Symbol("Secret");
myObj[secret] = "Bitcoin Wallet";

const fakeSecret = Symbol("Secret");
myObj[fakeSecret] // undefined

myObj[Symbol("Secret")] //undefined

myObj[secret] // "Bitcoin Wallet"

4. CANNOT BE ACCESSED (EASILY)

const image = getImageFromServer();
JSON.stringify(image)
// { name: '..', size: '..' }

const EMPTY = Symbol('empty')
image[EMPTY] = !image.url;

JSON.stringify(image)
// { name: '..', size: '..' }

GLOBAL SYMBOLS

 SYMBOL REGISTRY

const empty = Symbol("empty");
const globalEmpty = Symbol.for("empty");
const globalEmpty2 = Symbol.for("empty");

empty == globalEmpty // false
globalEmpty == globalEmpty2 // true

GLOBAL SYMBOLS

WELL KNOWN SYMBOLS (METAPROGRAMMING!)

Class FreshPrince {
  constructor() {
    this._innerArray = ['Will', 'Jazz'];
  }
  [Symbol.iterator]() {
    return this._innerArray[Symbol.iterator]();
  }
}
const prince = new FreshPrince();

for (let name of prince) {
 console.log(name); // Will, Jazz
}

prince instanceOf Array // false
prince instanceOf FreshPrince // true
Class FreshPrince {
  constructor() {
    this._innerArray = ['Will', 'Jazz'];
  }
}
Class FreshPrince {
  constructor() {
    this._innerArray = ['Will', 'Jazz'];
  }
  [Symbol.iterator]() {
    return /* ??? */
  }
}

Symbol.iterator

const prince = new FreshPrince();

for (let name of prince) {
 console.log(name); // ???
}

GLOBAL SYMBOLS

WELL KNOWN SYMBOLS

Class Carlton {
  constructor() {
    this._height = '1.5';
  }
  [Symbol.toPrimitive]() {
    return this._height;
  }
}
const carlton = new Carlton();

carlton == '1.5' // true
let i = 0;
Class Wtf {
  [Symbol.toPrimitive]() {
    return i++;
  }
}
const wtf = new Wtf ();

wtf == 0 && wtf == 1 && wtf == 2 // true

Symbol.toPrimitive

GLOBAL SYMBOLS

other WELL KNOWN SYMBOLS

Symbol.iterator

Symbol.match

Symbol.replace

Symbol.toPrimitive

Symbol.hasInstance

SYMBOLS!

MAKE JS GREAT AGAIN

THE

COLLECTIONS

how to remove duplicates from an array?

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})
function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}
function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

Keep track of selected images

1. Quickly find if an image is selected

2. Preserve order

3. No duplicates, of course

Keep track of selected images

const selected = {};
const selectedOrdered = [];

function selectImage(img) {
  selected[img.id] = true;
  selectedOrdered.push(img.id);
}

function unselectImage(img) {
  delete selected[img.id];
  selectedOrdered = selectedOrdered.filter(id => id!==img.id);
}
const selected = {};


function selectImage(img) {
  selected[img.id] = true;

}

function unselectImage(img) {
  delete selected[img.id];

}

set

const set = new Set();
const set = new Set([1, 2, 3]);
const set = new Set(elementList);

Iterable constructor

const set = new Set(['Homer', 'Bart', 'Homer']);
set.size; // 2

Removes duplicates

const set = new Set();
set.add('Homer')
   .add('Bart')
   .add('Homer')
   .add('Lisa');
for(let name of set){
  console.log(name);
  // 'Homer', 'Bart', 'Lisa'
}

preserves order

new Set(...)

Set

remove array duplicates

const unique = [...new Set(arr)];
const lorem = 'ipsum';
function test() {
 const a = myarr.map(()=> this.text != lorem.psium)
}
function removeUniques(arr) {
  const removeUniques  = () => arr.remove(this.unique !== this[i]);
  this.tester.call();
}
const selected = new Set();

function selectImage(img) {
  selected.add(img);
}

function unselectImage(img) {
  selected.delete(img);
}

function isSelected(img){
  return selected.has(img);
}

track selected

That fixes it!

now another problem!

Oh, I'm in no condition to drive! Wait a minute. I don't have to listen to myself. I'm drunk.

I'll keep it short and sweet honey —
Family. Religion. Friendship. These are the three demons you must slay if you wish to succeed in business.

 http://simpsons.dudaone.com/

you have a list of elements and you have to keep track on whos editing what

you have a list of elements and you have to keep track on whos editing what

Objects are not always ideal

1. keys can be only strings

2. hard to iterate

3. not trivial to count

4. does not preserve order

const editedIds = {}

function setEdited(element, user) {
  editedIds[element.id? element.name?] = user;
}
function whosEditing(element){
  return editedIds[element.id?];
}

Map

const map = new Map();
const map = new Map([
  ["Kids", 3],
  ["Why You Little", 10],
  ["Flanders", "Stupid"]
]);
const map = new Map(Object.entries({
  name: "name",
  age: 32
}));

Iterable constructor

new Map(...)

Map

const map = new Map();
map.set(user1, 'ADMIN');
map.set(user2, 'USER');
map.get(user1) // 'ADMIN'

map.set('100', 'str');
map.set(100, 'num');
map.get('100') // 'test'
map.get(100) // 'num'

objects as keys; SET/GET syntax

must have the
exact object key!

Map

PRESERVES ORDER

let recipeMap = new Map([
  [{name: 'cucumber'}, 500],
  [{name: 'tomatoes'}, 350],
  [{name: 'onion'}, 50]
]);
// keys
for (let vegetable of recipeMap.keys()) {
  console.log(vegetable); // {cucumber}, {tomatoes}, {onion}
}
// values
for (let amount of recipeMap.values()) {
  console.log(amount); // 500, 350, 50
}
// entries
for (let [vegetable, amount] of recipeMap.entries()) {...}

Oh, I'm in no condition to drive! Wait a minute. I don't have to listen to myself. I'm drunk!

I'll keep it short and sweet honey —
Family. Religion. Friendship. These are the three demons you must slay if you wish to succeed in business.

 http://simpsons.dudaone.com/
const editedElements = new Map();

function markEdited(element, user) {
  editedElements.set(element) = user;
}
function whosEditing(element){
  return editedElements.get(element);
}
function howManyEdited(){
  return editedElements.size;
}

Oh, I'm in no condition to drive! Wait a minute. I don't have to listen to myself. I'm drunk!

I'll keep it short and sweet honey —
Family. Religion. Friendship. These are the three demons you must slay if you wish to succeed in business.

 http://simpsons.dudaone.com/

I'll keep it short and sweet honey —
Family. Religion. Friendship. These are the three demons you must slay if you wish to succeed in business.

- EMpty -

editedMap

MEMORY LEAK!

the keys can't be collected

weak types

weak map/SET

SAME as a regular collection, but the keys are held weakly

const loggedUsers = new WeakSet();
let user = { name: 'Bart' }
let user2 = { name: 'Lisa'}
loggedUsers.add(user).add(user2);
delete user;

weak types

weak map/SET

very useful for DOM elements

to prevent memory leaks

const editedElements = new WeakMap();
editedElements.set(element1, data1);
editedElements.set(element2, data2);
element1.remove();

Oh, I'm in no condition to drive! Wait a minute. I don't have to listen to myself. I'm drunk!

I'll keep it short and sweet honey —
Family. Religion. Friendship. These are the three demons you must slay if you wish to succeed in business.

 http://simpsons.dudaone.com/

I'll keep it short and sweet honey —
Family. Religion. Friendship. These are the three demons you must slay if you wish to succeed in business.

- EMpty -

editedWeakMap

NEVER RISK A LEAK,

ALWAYS USE WEAK!!

weak types

weak map/SET

since the keys are held weakly, there is no way to get the value without the key

function getSecret() {
  const secrets = new WeakMap();
  const myKey = {/* some obj */};
  secrets.set(myKey, 'My Bitcoin Wallet');
  return secrets;
}
const secrets = getSecret();
// no way to get the value!

weak types

weak map/SET

there is no way to iterate weak types or even get their size

NEVER RISK A LEAK,

SOMETIMES USE WEAK!!

TO JAVASCRIPT!

the cause of, and solution to, all of life problems

"a fat function came into the store today"

A. Bundy

SHOES

Go get Nike 39

There are no Nikes

Go get Adidas 39

There are no Adidas

Go get Keds 39

Keds, but not 39

Go get Keds 38.5

SHOES

Go get Nike 39

No Nike!

Get
Adidas!

No Adidas!

Get
Keds!

No size 39!

Get
38.5!

JS "run to completion"

function getNames() {
  const value1 = 'Bud';
  while(veryLongTime) {
    ...
  }
  const value2 = 'Kelly';
  return [value1, value2];
}
let names;
names = getNames();
console.log(names[0]);
// unrelated stuff
// ...
console.log(names[1]);

A new keyword !

yield

   returns control to the caller

function *namesGen() {
  let i = 0;
  yield 'Bud';
  i++;
  yield 'Kelly';
  i++;
  return i;
}
let names;
names = namesGen();
// ... some stuff
console.log(names.next().value);
// ... some more stuff
console.log(names.next().value);
// ... do things
console.log(names.next().value);
// 'Bud'
// 'Kelly'
// 2

GENERATRORS, man

GENERATORS ALLOW YOU TO CONTROL FLOW BETWEEN CALL FRAMES

next() returns the next yield value

function *tvShows() {
  yield 'Psycho Dad';
  yield 'Psycho Dad 2';
  return 'Psycho Dad 3';
}
const tv = tvShows();
tv.next() // { value: 'Psycho Dad', done: false }
tv.next() // { value: 'Psycho Dad 2', done: false }
tv.next() // { value: 'Psycho Dad 3', done: true }
const tv = tvShows();
const tv = tvShows();
tv.next() // { value: 'Psycho Dad', done: false }
const tv = tvShows();
tv.next() // { value: 'Psycho Dad', done: false }
tv.next() // { value: 'Psycho Dad 2', done: false }

infinite iteration

GENERATRORS

function *fiboGen() {
  let a = 0;
  let b = 1;
  while(true) {
    yield b;
    [a, b] = [b, a + b];
  }
}
const f = fiboGen();
console.log(f.next().value);
console.log(f.next().value);
onClick(() => {
  console.log(f.next().value);
});

1

1

2

3

5

8

13

21

34

55

complex iteration

GENERATRORS

function *processTree(data){
  if (!data) { return; }

  for (var i = 0; i < data.length; i++) {
    var val = data[i];
    yield(val);
    yield *processTree(val.children);
  }
}
const data = processTree(tree);
let item = data.next();
while(!item.done){
  console.log(item.value.id);
  item = data.next();
}

1

1.1

1.2

1.3

1.2.1

2

2.1

1.2.2

complex iteration

GENERATRORS

function *processTree(data){
  if (!data) { return; }

  for (var i = 0; i < data.length; i++) {
    var val = data[i];
    yield(val);
    yield *processTree(val.children);
  }
}

N

O

M

A

A

M

!

'

const data = processTree(tree);
let item = data.next();
while(!item.done){
  console.log(item.value.id);
  item = data.next();
}

passing data to NEXT()

GENERATRORS:

data pass - blackjack

GENERATRORS

function *easyBlackJack(){
  let result = 0;
  let oneMore = true;
  while(oneMore) {
    let card = Math.random(10);
    result += card;
    if(result <= 21) {
      oneMore = yield card;
    } else {
      return `${card}...Bummer.`;
    }
  }
  return `You got a total of ${result}`;
}
const game = easyBlackJack();
let card = ebj.next();

while(!card.done) {
  const hitMe = card.value < 9;
  card = game.next(hitMe);
}
console.log(card.value)

7

true

3

true

9

false

19 total

promises promises

function *promisesGenerator(){
  const name = yield getNameFromServer();
  let age;
  if(name) {
    age = yield getAgeFromServer(name);
  }
  return [name, age];
}
const p = promisesGenerator();
const namePromise = p.next().value;
const agePromise = namePromise.then(name => p.next(name).value);
const resultPromise = agePromise.then(age => p.next(age).value);
resultPromise.then(result => console.log(result));

promises promises

function *promisesGenerator(){
  const name = yield getNameFromServer();
  let age;
  if(name) {
    age = yield getAgeFromServer(name);
  }
  return [name, age];
}
async function promisesGenerator(){
  const name = await getNameFromServer();
  let age;
  if(name) {
    age = await getAgeFromServer(name);
  }
  return [name, age];
}

Js is beautiful... (from the inside)

proxies

proxies

A Long long time ago

const myObj = {};
Object.defineProperty(myObj, 'name', {
  get: function(key){
    console.log('Name was called');
    return 'Chanandler Bong';
  }
});

var x = myObj.name; // Name was called
console.log(x); // Chanandler Bong
const myObj = {};
Object.defineProperty(myObj, 'name', {
  get: function(key){
    return this._name;
  },
  set: function(key, value) {
    this._name = 'Ms. ' + value;
  }
});

myObj.name = 'Chanandler Bong';
console.log(myObj.name); // Ms. Chanandler Bong

Object.defineProperty

const friends = { Joey, Chandler, Rachel };

Object.seal(friends);
friends.Ross = 'Ross'; // Exception - cannot add to a sealed object
friends.Rachel = 'Monica'; // ok

const friends2 = Object.freeze(friends);
friends2.Chandler = 'Let it go'; // Exception - cannot change a frozen object

Object.seal/freeze

const friends = { Joey, Chandler, Rachel };

Object.seal(friends);
friends.Ross = 'Ross'; // Exception - cannot add to a sealed object
friends.Rachel = 'Monica'; // ok

Pretty lame.

But now....

PROXIES!!!

A proxy is an interface that intercepts all calls to an object

const originalObject = { name: 'Gunther' };
const handler = {};

const proxiedObject = new Proxy(originalObject, handler);

proxiedObject.name // Gunther
proxiedObject.age = 55;
proxiedObject.age // 55

Default Proxy

const handler = {
  get: function(target, key) {
    // called when any property is accessed
  },
  set: function(target, key, value) {
    // called when any property is changed
  },
  has: function(target, key) {
    // called when checking (key in target)
  },
  deleteProperty: function(target, key) {
    // called when any property is deleted
  },
  apply: function(target, thisArg, args) {
    // called when the object is invoked as a function
  }
};

traps

7 use cases

1. default properties

const handler = {
  get: function(target, key){
    return Reflect.get(target, key) || 'No such friend';
  }
};

const friends = new Proxy({}, handler);

console.log(proxiedObject.rachel) // 'No such friend'
proxiedObject.rachel = 'Rachel';
console.log(proxiedObject.rachel) // 'Rachel'
console.log(proxiedObject.emily) // 'No such friend'

1. default properties

const handler = {
  get: function(target, key){
    return Reflect.get(target, key) || 'No such friend';
  }
};

2. private property

const handler = {
  get: function(target, key){
    if(key.startsWith('_')){
      throw new Error('Get out you filthy hippie!!');
    }
    return Reflect.get(target, key);
  }
};

const c = {name: 'Chandler', _fear: 'Puppies'};
const chandler = new Proxy(c, handler);

console.log(chandler.name) // 'Chandler'
console.log(chandler._fear) // Error: Get out you filthy hippie!!
console.log(chandler._nickname) // Error: Get out you filthy hippie!!

2. private property

const handler = {
  get: function(target, key){
    if(key.startsWith('_')){
      throw new Error('Get out you filthy hippie!!');
    }
    return Reflect.get(target, key);
  }
};

3. autosave to cookie

const handler = {
  set: function(target, key, value) {
    document.cookie = `${prop}=${value}`;
    Reflect.set(target, key, value);
  },
  deleteProperty: function(target, key) {
    document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
    return Reflect.deleteProperty(target, key);
  }
}

const myCookies = new Proxy({}, handler);

myCookies.USER = 'Richard';
myCookies.VISITS = 99;
console.log(document.cookie);  // USER=Richard; VISITS=99

3. autosave to cookie

const original = { ross, joey, chandler }

const handler = {
  set: function(target, key, value){
    if(key == 'ross' || key == 'joey' || key == 'chandler') {
      throw 'You cant change one of the originals!'
    } else {
      Reflect.set(target, key, value);
    }
  }
}

const friends = new Proxy(original, handler);
friends.rachel = 'Rachel'; // ok
friends.joey = 'Drake Ramore'; // Error: You can't change...!

4. set traps

{
  set: function(obj, prop, value) {
    if (prop === 'name') {
      if (typeof value !== 'string' || value === '') {
        throw new Error('"name" should be a non-empty string');
      }
    } else if (prop === 'age') {
      if (typeof value !== 'number' || value < 0 || value > 200) {
        throw new Error('"age" should be a number between 0 and 200');
      }
    } else {
      throw new Error('The only valid options are "name" and "age"');
    }
    Reflect.set(obj, prop, value);
  }
}

5. props validation

friend.name = 'Joey';
friend.age = 30;

friend.sisters = 8;
// TypeError: The only valid options are
// "name" and "age"

friend.age = 'Why God Why';
// TypeError: "age" should be a number
// between 0 and 200
function Family() {
  return new Proxy({}, handler);
}

const handler = {
  get: function (target, key, receiver) {
    if (!(key in target)) {
      target[key] = Family();  // auto-create a sub-Tree
    }
    return Reflect.get(target, key, receiver);
  }
};

6. auto subpaths!

const family = Family();
log(family) // {}

family.child1.child2.name = 'Emma';
log(family) // { child1: { child2: { name: 'Emma' }}}

family.child1.child3.name = 'Ben';
log(family) // { child1: { child2: { name: 'Emma' }}}
            //           { child3: { name: 'Ben' }}}
const parts = [];
const google = new Proxy(function(){}, {
  get: function (object, prop) {
    parts.push(prop);
    return proxy;
  },
  apply: function() {
    var returnValue = 'http://google.com/' + parts.join('/');
    parts = [];
    return returnValue;
  }
});

6. auto subpaths

google.search.products.mac.and.cheese()
// http://google.com/search/products/mac/and/cheese

google.search.ducks.or.clowns()
// http://google.com/search/ducks/or/clowns

7. extend properties

const myArr = ['Joey', 'Chandler', 'Monica'];
const coolArray = new Proxy(myArr, {
  get: function (array, ind) {
    if(ind < 0) {
      return Reflect.get(array, array.length + (1*ind));
    } else {
      return Reflect.get(array, ind);
    }
  }
});

7. extend properties

coolArray[2]; // 'Monica'
coolArray[-1]; // 'Monica'

coolArray[3] = 'Ross'
coolArray[-1]; // 'Ross'
const original = { place: 'Central Perk' };
const handler = {
  set: function(target, key, value) {
    target._copy = target._copy || {...target};
    Reflect.set(target._copy, key, value);
  },
  deleteProperty: function(target, key) {
    target._copy = target._copy || {...target};
    Reflect.deleteProperty(target._copy, key);
  },
  get: function(target, key){
    if(key === 'commit') {
      return () => new Proxy(target._copy, handler);
    }
    return Reflect.get(target, key);
  }
}

8. immutability

let obj = new Proxy(original, handler);
obj.place = 'Times Square';
obj.place // 'Central Perk'
obj = obj.commit();
obj.place // 'Times Square'

delete obj[place];
obj.place // 'Times Square'
obj = obj.commit();
obj.place // undefined

original.place // 'Central Perk'
const original = { place: 'Central Perk' };
const handler = {
  set: function(target, key, value) {
    target._copy = target._copy || {...target};
    Reflect.set(target._copy, key, value);
  },
  deleteProperty: function(target, key) {
    target._copy = target._copy || {...target};
    Reflect.deleteProperty(target._copy, key);
  }
}
const original = { place: 'Central Perk' };
const handler = {
  set: function(target, key, value) {
    target._copy = target._copy || {...target};
    Reflect.set(target._copy, key, value);
  }
}

Can I use it???

Symbols

COLLECTIONS

GENERATORS

PROXIES

ES6

Guide

ES

19:30

20:00

20:30

21:00

QUESTIONS?

ES6+

By Liad Yosef

ES6+

Hidden Gems in ES6

  • 137
Loading comments...

More from Liad Yosef