4

I'm trying to learn js and trying to extend a Map. I did:

function mapExtend(mapInstance) {
  function MapExtend(){
  }

  MapExtend.prototype = Object.create(Map.prototype);
  MapExtend.prototype.constructor = MapExtend;

  return new MapExtend(mapInstance)
}

And I did this:

const b = new Map()
mapExtend(b).get(1)

I'm getting the following error:

Uncaught TypeError: Method Map.prototype.get called on incompatible receiver #<MapExtend>
    at MapExtend.get (<anonymous>)
    at <anonymous>:1:14

What mistake i'm doing here?

Bahman Parsa Manesh
  • 2,314
  • 3
  • 16
  • 32
batman
  • 3,565
  • 5
  • 20
  • 41

3 Answers3

4

I can't give you a explanation right now, because I need to verify my assumption first.

But extenting is possible using the ES6 syntax:

function mapExtend(mapInstance) {
  class MapExtend extends Map {}

  return new MapExtend(mapInstance)
}

const b = new Map()
mapExtend(b).get(1)
t.niese
  • 39,256
  • 9
  • 74
  • 101
  • Yes this is doable :) But what wondering why the same is not possible via functions.. :) – batman Aug 25 '18 at 08:29
  • @batman I guess it will work if you can call the constructor of the `Map` in the context of the instance of your `MapExtend` object (`Map.apply(this, arguments)`. I guess it does some necessary initialisations, but I can't confirm this assumption right now. But calling the constructor does not work, because then the engine will complain that the constructor must only be invoced using `new`. – t.niese Aug 25 '18 at 08:31
  • For the record: https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf – KooiInc Aug 25 '18 at 11:34
1

You can extend the prototype of native Objects directly. Not everyone considers that good practice

Map.prototype.reportSize = function () {
    return `This map contains ${this.size} ${this.size === 1 ? "entry" : "entries"}`;
};

var x = new Map();
console.log(x.reportSize());
x.set(3, "three");
console.log(x.reportSize());

Alternatively you can create a custom function, using extensions within it. This way you don't have to extend the prototype of Map

const MapWithExtensions = map => {
  return {
    map: map,
    reportSize: function () {
     return `This map contains ${this.map.size} ${
      this.map.size === 1 ? "entry" : "entries"}`; 
    }
  };
};
const myMap = MapWithExtensions(new Map);
console.log(myMap.reportSize());
myMap.map.set(9, "nine");
console.log(myMap.reportSize());
console.log(myMap.map.get(9));

Finally, this may be a way to create an extended Map without extending the Map prototype (actually, it maps the Map.prototype keys to methods within the extended Map).

const xMap = MapFactory({
  mySize: function() {return `Hi. My size is currently ${this.size}`}
});
const myMap = xMap.Create();
console.log(myMap.mySize());
console.log("setting [5, \"five\"]")
myMap.set(5, "five");
console.log(myMap.mySize());
console.log(myMap.entries().next().value);

function MapFactory(extensions = {}) {
  const proto = new Map;
  const mappings = Object.getOwnPropertyNames(Map.prototype)
    .reduce( (reduced, key) => { 
      if (proto[key].constructor !== Function) {
        reduced.localProps.push(key);
      } else {
        reduced.proto[key] = function (...args) { return this.map[key](...args); };
      }
      return reduced;
    },
    { localProps: [], proto: {} }
  );
  const XMap = function (map) {
      this.map = map;
      mappings.localProps.forEach( prop =>
        Object.defineProperty(this, prop, { get() {return this.map[prop]; }}) 
      );
  };
  XMap.prototype = {...mappings.proto, ...extensions};
  return { Create: (map = new Map) => new XMap(map) };
}
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • Extending the prototype of native Objects is never a good idea. You should only do that if you want to polyfill features that are already specified but not implemented by the engine. – t.niese Aug 25 '18 at 08:29
0

You could also directly manipulate the prototype of the map, like this:

let collection =  new Map ();

Map.prototype.customFunc = () => {console.log('customFunc')}

collection.customFunc();
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155