Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly extend ES6 Map

I have a simple case: a ES6 Map, and I need to add custom get() and set() to it.

But Map is a built-in object, so I'm not sure whether there would be any caveats in doing so. I've tried to search whether it is correct to subclass a Map, and got inconsistent results: it is unclear whether it's allowed by specification, what browser/node.js versions support it, and what side-effects are possible (and what to cover with tests).

As I understand, there are three main approaches to extend Map functionality:

  1. Subclass it. That I've done, and it seems like it works.
class CustomMap extends Map{
    get(key){
        return super.get(key);
    }
    set(key, value){
        return super.set(key, value);
    }
}

Problem with it: a lot of articles on the Internet state that you can run into troubles with extending built-in objects. Most are early 2016, and now is late 2017, testing in Chrome 61. Maybe now it is a safe and supported way of doing it?

  1. Make a wrapper object
const Wrapper = function(){
    this._map = new Map();
    this.get = (key) => {return this._map.get(key);}
    this.set = (key, value) => {this._map.set(key, value);}
    ... everything else
}

The least elegant solution, as I need to implement not just get and set, but all of Map functionality. Also, Wrapper is not an instance of Map.

  1. Use ES6 Proxy
const ProxyMap = function(){
    return new Proxy(new Map(), {
        get(target, key){
            return target.get(key)
        }
        set(target, key, value){
            target.set(key, value);
        }
    }
}

As with extending a class, it is unadvisable to apply Proxy to some built-in types. But again, a lot of time passed since introducing the Proxy specifications; maybe now Map could by proxied in modern browsers?

So, the question is: what way of extending a Map is a correct and robust way in 2017?

like image 640
Justin Trevein Avatar asked Oct 19 '17 16:10

Justin Trevein


People also ask

How do you extend in JavaScript?

The extends keyword can be used to extend the objects as well as classes in JavaScript. It is usually used to create a class which is child of another class. Syntax: class childclass extends parentclass {...}

What are the advantages of using ES6 maps over objects?

Prior to the introduction of Maps in ES6, objects were generally used to hold key-value pairs. Maps have advantages over objects when creating hash maps because: You can use different data types (i.e., primitives, objects, functions) as keys. You can easily get the size of a map through it's size property.

Are ES6 maps ordered?

Maps are inherently not ordered (except the are iterated insertion order, which requires sorting and then [re-]adding).

Do JavaScript maps maintain order?

The keys in Map are ordered in a simple, straightforward way: A Map object iterates entries, keys, and values in the order of entry insertion. Although the keys of an ordinary Object are ordered now, this was not always the case, and the order is complex. As a result, it's best not to rely on property order.


2 Answers

It is unclear whether it's allowed by specification

It is. Since ES6, all builtin types are extensible using class syntax

It is unclear what browser/node.js versions support it

They need to support ES6 classes and Map natively. Using a transpiler will usually break it.

1) Subclass it. That I've done, and it seems like it works.

Yes, that is the correct approach.

a lot of articles on the Internet state that you can run into troubles with extending built-in objects. Most are early 2016, and now is late 2017, testing in Chrome 61.

I dunno, the major reference http://perfectionkills.com/extending-native-builtins/ is from 2011. And these articles meant a different thing by "extending builtins": amending their prototype objects with custom objects, e.g. Map.prototype.getWithDefault = function(…) { … };. They do not refer to class … extends ….

Make a wrapper object

This should be fine as well. I don't think you necessarily need your instances to be instanceof Map, if you do you'd have to follow the Liskov substitution principle. Not all "extensions" of a key-value collection would fit that.

3) Use ES6 Proxy - it is unadvisable to apply Proxy to some built-in types.

Indeed, this doesn't work or is at least cumbersome.

like image 154
Bergi Avatar answered Sep 28 '22 21:09

Bergi


The first one is the way to go. class syntax is supported with ES6, as well as Maps and extending Maps is part of this initial defininition too. So every system that supports Maps supports the first method, and the second and the third are just ugly ( concerning performance etc.)

like image 27
Jonas Wilms Avatar answered Sep 28 '22 20:09

Jonas Wilms