Piotr Kowalski @piecioshka
 
         
            Cyfrowy Polsat, Warsaw
                JavaScript Ninja. Mac lover. Open source fan.
                Blogger.
                Organizer WarsawJS.
                Author of several libs in @npm
            
"Kto chce szuka sposobu, kto nie chce szuka powodu."
            
                let MAP = {}
                let foo = { name: 'foo' }
                let bar = { name: 'bar' }
                MAP[foo] = "Hello"
                MAP[bar] = "World"
                console.log(MAP[foo]) // ???
            
        
        My interview question.
            
                let MAP = new Map()
                let foo = { name: 'foo' }
                let bar = { name: 'bar' }
                MAP.set(foo, "Hello")
                MAP.set(bar, "World")
                console.log(MAP.get(foo)) // ???
            
        
    
            SIMD:
            
                Float32Array,
                Float64Array,
                Int8Array,
                Int16Array,
                Int32Array,
                Uint8Array,
                Uint8ClampedArray,
                Uint16Array,
                Uint32Array
            
        
            DOM:
            
                DOMStringList,
                DOMStringMap,
                DOMTokenList,
                HTMLCollection,
                HTMLAllCollection,
                HTMLFormControlsCollection,
                HTMLOptionsCollection,
                NodeList,
                RadioNodeList,
                SVGAnimatedLengthList,
                SVGAnimatedNumberList,
                SVGAnimatedTransformList,
                SVGLengthList,
                SVGNumberList,
                SVGPointList,
                SVGStringList,
                SVGTransformList
            
        
            Misc:
            
                ArrayBuffer
                AudioTrackList,
                CSSRuleList,
                ClientRectList,
                DataTransferItemList,
                FileList,
                MediaKeys,
                MediaList,
                MediaQueryList,
                MimeTypeArray,
                PluginArray,
                SourceBufferList,
                SpeechGrammarList,
                StylePropertyMap,
                StyleSheetList,
                TextTrackCueList,
                TextTrackList,
                TouchList,
                TrackDefaultList,
                VTTRegionList,
                VideoTrackList
            
        
ObjectArrayMap ✨Set ✨WeakMap ✨WeakSet ✨Map or Set every day⁉️| ... | Map | Set | WeakMap | WeakSet | 
|---|---|---|---|---|
| Chrome | 38 | 38 | 36 2014-07 | 36 | 
| Firefox | 13 | 13 | 6 | 34 | 
| IE | 11 | 11 | 11 | x | 
| Opera | 25 | 25 | 23 | 23 | 
| Safari | 7.1 | 7.1 | 7.1 | 9 | 
MapThe Map object is a simple key/value map. Any value (both objects and primitive values) may be used as either a key or a value. MDN @ 2016
            
                // #1: API: Hashmap
                // --------------------------------------------------
                let map = new Map()
                map.set('foo', 'bar')
                // instead of: object[key] = value
                map.size // 1
                // instead of: array.length
                map.get('foo') // 'bar'
                 // instead of: object[key]
            
        
    
            
                // #2: API
                // --------------------------------------------------
                // Remove pair key/value (by reference or primitive)
                map.delete(key) // instead of: array.splice(0, 1)
                // Clear collection
                map.clear() // instead of: array.length = 0
            
        
    
            
                // #3: API
                // --------------------------------------------------
                // Check if key is used in collection
                map.has(key)
                // instead of:
                object.hasOwnProperty(key) // or: key in object
                array.indexOf(value) !== -1
                array.includes(value) // ES2016 (a.k.a. ES7) way
            
        
    
            
                // #4: API: Magic
                // --------------------------------------------------
                // Get pair key/value form collection {MapIterator}
                map.entries() // similar to: Object.entries(object)
                // Get keys from collection {MapIterator}
                map.keys() // similar to: Object.keys(object)
                // Get values from collection {MapIterator}
                map.values() // similar to: Object.values(object)
            
        
    
            
                // #5: API: Simple iteration
                // --------------------------------------------------
                // Iteration through collection
                map.forEach((value, key) => /* ... */)
                // the same in arrays
                // New loop `for..of` (ES2015)
                for (let [key, value] of map.entries()) {
                    map.delete(key) // true
                }
            
        
    
            
                // #6: API: WTF?
                // --------------------------------------------------
                let map = new Map()
                map.set() // Map { undefined => undefined }
                map.size // 1
                // In arrays
                let array = []
                array.push()
                array.length // 0
            
        
    
            
                // #7: API: "Holy" couple key/value
                // --------------------------------------------------
                let map = new Map()
                let rand = Math.random()
                map.set([1, 2, 3], Math.random())
                map.set([1, 2, 3], Math.random())
                map.set([1, 2, 3], rand)
                map.set([1, 2, 3], rand)
                map.size // 4
            
        
    
            
                // #8: API: .. but unique key!
                // --------------------------------------------------
                let map = new Map()
                let rand = Math.random()
                map.set('item', Math.random())
                map.set('item', rand) // overwrite previous
                map.size // 1
                map // Map {"item" => 0.199...}
            
        
    
            
                // #9: API: Adding without coercing
                // --------------------------------------------------
                let map = new Map()
                map.set(5, 'foo')
                map.set("5", 'bar')
                console.log(map.size) // 2
            
        
    
            
                // #1: Example: Array as key
                // --------------------------------------------------
                let map = new Map()
                map.set(['win8', 'moz'], 'a').set(['xp', 'chrome'], 'b')
                function check(collection, ...browsers) {
                    for (let [key, value] of collection)
                        // "xp,firefox" === "win8,moz"
                        if (String(browsers) === String(key))
                            return value
                }
                check(map, 'xp', 'chrome') // 'b'
            
        
    
            
                // #2: Example: Async iteration
                // --------------------------------------------------
                map.set({ foo: 1 }, 'a').set({ foo: 2 }, 'b')
                let iterator = map.values() // {MapIterator}
                let interval = setInterval(() => {
                    let item = iterator.next()
                    if (item.done) {
                        clearInterval(interval)
                        return
                    }
                    console.log(item.value) // 'a' => [ONE SECOND] => 'b'
                }, 1000)
            
        
    
            
                // #3: Example: Subclassing of Map
                // --------------------------------------------------
                class MultiMap extends Map {
                    set(...args) {
                        args.map(([key, value]) => super.set(key, value))
                        return this
                    }
                }
                let map = new MultiMap()
                map.set(['key', 'value'], ['key2', 'value2'])
                // Map {"key" => "value", "key2" => "value2"}
            
        
    Map is cool⁉️SetThe Set object lets you store unique values of any type, whether primitive values or object references. MDN @ 2016
            
                // #2: API
                // --------------------------------------------------
                let set = new Set([1, 2, 3, 4])
                set.add(value) // instead of: array.push(value)
                // NOT: set.set() // ??? undefined
                // Removing is by value (or reference), not index.
                set.delete(3) // true
                set.clear() // ...and we have empty Set
                set.has(value) // Check by reference
                set.get(key) // TypeError: set.get is not a function
            
        
    
            
                // #3: API: Magic
                // --------------------------------------------------
                // Get values from collection {SetIterator}
                set.entries()
                set.keys()
                set.values()
                // hmmm...
                // set.values() and set.keys() returns same Iterator
            
        
    
            
                // #6: API: Simple iteration
                // --------------------------------------------------
                let set = new Set([1, 1, 2])
                set.forEach((item) => {
                    console.log(item) // 1, 2
                })
            
        
    
            
                // #1: Example: AssetsLoader: loadImage helper
                // --------------------------------------------------
                function loadImage(path /* string */) {
                    return new Promise((resolve, reject) => {
                        let image = new Image()
                        let on = image.addEventListener
                        on('load', () => resolve(image))
                        on('error', () => reject(image))
                        image.src = path
                    })
                }
            
        
    
            
                // #1: Example: AssetsLoader: test case
                // --------------------------------------------------
                let al = new AssetsLoader()
                al.addImage('../assets/images/plants.jpg')
                al.addImage('../assets/images/sky.jpg')
                al.loadImages()
                    .catch((error) => {
                        console.error(error)
                    })
                    .then((images) => {
                        console.info('Loaded successful', images)
                    })
            
        
    
            
                // #1: Example: AssetsLoader v1
                // --------------------------------------------------
                class AssetsLoader {
                    constructor() {
                        this._images = new Set()
                    }
                    addImage(path /* string */) {
                        return this._images.add(loadImage(path))
                    }
                    loadImages() {
                        return Promise.all(this._images)
                    }
                }
            
        
    
            
                // #2: Example: AssetsLoader v2: Subclassing Set
                // --------------------------------------------------
                class AssetsLoader {
                    constructor() {
                        this._images = new Images()
                    }
                    addImage(path /* string */) {
                        return this._images.add(path)
                    }
                    loadImages() {
                        return this._images.load()
                    }
                }
            
        
    
            
                // #2: Example: AssetsLoader v2: Subclassing Set
                // --------------------------------------------------
                class Images extends Set {
                    add(path) {
                        return super.add(loadImage(path))
                    }
                    load() {
                        return Promise.all(this.keys())
                    }
                }
            
        
    
            
                // #3: Example: Remove duplicated item (Array)
                // --------------------------------------------------
                let array = [1, 2, 3, 1, 3]
                // Constructor expects another collection
                [...new Set(array)] // [1, 2, 3]
            
        
    
            
                // #3: Example: Remove duplicated items (Map)
                // --------------------------------------------------
                let map = new Map()
                map.set({ foo: 1 }, 'a').set({ foo: 2 }, 'b')
                [...new Set(map.keys())]
                // [{ foo: 1 }, { foo: 2 }]
                [...new Set(map.values())]
                // ['a', 'b']
            
        
    Set is cool⁉️WeakMapThe WeakMap object is a collection of key/value pairs in which the keys are weakly referenced. The keys must be objects and the values can be arbitrary values. MDN @ 2016
            
                // #1: API: Simple API
                // --------------------------------------------------
                let weakMap = new WeakMap()
                let key = { foo: 1 }
                weakMap.set([1], 5) // WeakMap { [1] => 5 }
                weakMap.set(key, 6) // WeakMap { [1] => 5, {foo: 1} => 6 }
                weakMap.get(key) // 6
                weakMap.has(key) // true
                weakMap.delete(key) // true
            
        
    
            
                // #2: API: "Holy" couple key/value
                // --------------------------------------------------
                let weakMap = new WeakMap()
                weakMap.set([1, 2], Math.random())
                weakMap.set([1, 2], Math.random())
                weakMap.set([1, 2], Math.random())
                weakMap.size // ??? undefined
                weakMap // WeakMap {[1, 2] => 0.609..., [1, 2] => 0.268..., [1, 2] => 0.183...}
            
        
    WeakMap keys can ONLY be objects⁉️
            
                // #3: API: How it works?
                // --------------------------------------------------
                let users = [{ name: 'Peter' }, { name: 'Kate' }]
                let weakMap = new WeakMap()
                weakMap.set(users[0], 'some text 1')
                weakMap.set(users[1], 'some text 2')
                console.log(weakMap.get(users[0])) // 'some text 1'
                users.splice(0, 1)
                console.log(weakMap.get(users[0])) // 'some text 2'
            
        
     
    ☟︎ 2016-09-12 11:38:43.753 ☟︎
 
        ☝︎ 2016-09-12 11:38:59.656 ☝︎
 
    WeakMap is cool⁉️WeakSetThe WeakSet object lets you store weakly held objects in a collection. MDN @ 2016
            
                // #1: API: Simple API
                // --------------------------------------------------
                let weakSet = new WeakSet()
                // Adding ONLY objects
                weakSet.add([1])
                weakSet.add(1)
                // TypeError: Invalid value used in weak set
                // ... by reference (not value)
                weakSet.has([1]) // false
                weakSet.delete([1]) // false
            
        
    
            
                // #2: API: How it works?
                // --------------------------------------------------
                let users = [{ name: 'Peter' }, { name: 'Kate' }]
                let weakSet = new WeakSet()
                weakSet.add(users[0])
                weakSet.add(users[1])
                console.log(weakSet) // WeakSet {Object {name: "Kate"}, Object {name: "Peter"}}
                users.splice(0, 1)
                console.log(weakSet) // WeakSet {Object {name: "Kate"}, Object {name: "Peter"}}
            
        
     
    ☟︎ 2016-09-12 15:13:09.329 ☟︎
 
        ☝︎ 2016-09-12 15:13:17.837 ☝︎
 
    WeakSet is cool⁉️Map & Set can use primitives and objects as keysSet & WeakSet has unique keysWeakMap & WeakSet can use ONLY objects as keysWeakMap & WeakSet don't have size & forEach propertyWeakMap & WeakSet held items weakly, so GC will remove its when are unused
            
                // Symbol(Symbol.toStringTag)
                String(new Map) === "[object Map]"
                String(new Set) === "[object Set]"
                String(new WeakMap) === "[object WeakMap]"
                String(new WeakSet) === "[object WeakSet]"
            
        
    | Type | Operations / seconds | 
|---|---|
| Object | 2,590,044ops/sec ±6.32% (72 runs sampled) | 
| Array | 675,882ops/sec ±13.49% (65 runs sampled) | 
| Map | 152,399ops/sec ±9.67% (63 runs sampled) | 
| Set | 184,469ops/sec ±6.27% (78 runs sampled) | 
| WeakMap | 199,211ops/sec ±1.93% (82 runs sampled) | 
| WeakSet | 202,026ops/sec ±1.91% (82 runs sampled) | 
| Type | usedJSHeapSize | 
|---|---|
| Object | 7.977 KB | 
| Array | 5.281 KB | 
| Map | 28.625 KB | 
| Set | 13.219 KB | 
| WeakMap | 15.844 KB | 
| WeakSet | 9.484 KB | 
            
                window.name = { foo: 1 }
                console.log(window.name) // [object Object]
                // what happen here?
                // [immutable type]
                const object = {}
                object.a = 1
                console.log(object.a) // 1
                // why we can do this?
                // [immutable reference]
            
        
    
            
                typeof null === 'object' // true
                // why?
                // [empty reference to object]
                Number.isFinite('1.2') // false | ES6
                isFinite('1.2') // true | ES3
                // OMG
                // [more robust]
                typeof class {} === "function" // true
                // why?
                // [syntactic sugar]
            
        
    
            
                Object.observe(object, (changes) => {
                    console.log(changes)
                })
                // but today...
                Object.observe // undefined
                // what can I do? use Proxy!
            
        
    
            
                let object = {}
                object = new Proxy(object, {
                    set: (o, prop, value) => {
                        console.warn(`${prop} is set to ${value}`)
                        o[prop] = value
                    },
                    get: (o, prop) => {
                        console.warn(`${prop} is read`)
                        return o[prop]
                    }
                })
            
        
    