2

To create a map in ES6, should you say Map() or new Map()?

Both seem to work fine in node --harmony.

The draft spec says "The Map constructor is the %Map% intrinsic object and the initial value of the Map property of the global object. When Map is called as a function rather than as a constructor, it initializes its this value with the internal state necessary to support the Map.prototype built-in methods." which also seems to suggest they should both work.

Given that, Map() would seem better because shorter, though that's a subjective judgment; objectively they seem interchangeable?

rwallace
  • 31,405
  • 40
  • 123
  • 242
  • That is a lot like a question of Date() or new Date(). It depends on what you will be attempting to do. – Wayne Aug 15 '14 at 08:31
  • @Wayne Okay, which things one might be attempting to do, make one usage preferable to the other? – rwallace Aug 15 '14 at 08:32
  • The documentation suggests one will always use new MAP() ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map ... – Wayne Aug 15 '14 at 08:44
  • Date() has methods like now, which can be useful without creating a new date object. Map() and Array() both are used to hold data, which is not done in the global object, but rather are it is the constructor that creates the methods. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ... http://stackoverflow.com/questions/1114024/constructors-in-javascript-objects ... the methods are returned functions. – Wayne Aug 15 '14 at 08:58

2 Answers2

7

Although current browsers appear to permit the use of Map() without new (as shown in @RobG's answer), this is in fact incorrect.

Because of a stability issue, Node.js v0.10 uses an older version of V8 (the JavaScript engine in Node.js) which does not throw if you do Map().

Node.js v0.10 uses V8 v3.14.5, but the issue with Map() not throwing was only fixed in v3.20.12.

Chrome, which uses a newer version of V8, throws an error if you don't use new. I'm not sure why Firefox doesn't throw an error though.


According to the current draft specification, it should be impossible to call Map() without new:

  1. Let map be the this value.
  2. If Type(map) is not Object then, throw a TypeError exception.
  3. If map does not have a [[MapData]] internal slot, then throw a TypeError exception.
  4. If map’s [[MapData]] internal slot is not undefined, then throw a TypeError exception.

Usually in a constructor the object that is supposed to be created is this. It requires that the object passed is an instance of Map, and that it has not been initialised yet ([[MapData]] must be undefined).

Apparently this is because it hinders the subclassing of native constructors, which will be possible in ES6.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
  • 1
    To me the (draft) specification is not clear when §23.1.1 says: "When **Map** is called as a function rather than as a constructor, it initializes its **this** value with the internal state necessary to support the **Map.prototype** built-in methods". Doesn't that mean it creates an instance of *Map*? If called as a function without setting its *this*, isn't it set to the global object? I dunno. But it does say "if *iter* is undefined, then return map", and calling `Map()` returns an instance. Given their influence and involvement in ES6, I doubt Mozilla would fluff their implementation, but… – RobG Aug 15 '14 at 10:34
  • @RobG: the "this" value used by native functions does not convert undefined/null to the global object, so it is `undefined` by default. What it is saying is technically correct, but it will only run in the first place if the "this" value is an uninitialised `Map` instance created by the constructor . I would agree that the ES6 specification language is not particularly easy to read, especially when compared to the ES5 specification. – Qantas 94 Heavy Aug 15 '14 at 10:37
  • 1
    @RobG this is a lot clearer if you read https://github.com/rwaldron/tc39-notes/blob/master/es6/2013-07/july-25.md#anti-pattern-to-call-a-constructor-without-new – Benjamin Gruenbaum Aug 19 '14 at 06:26
  • 1
    +1 this last comment should find its way in the answer – Denys Séguret Aug 19 '14 at 06:43
3

Edit

As of ECMAScript 2015 (ed. 6), calling Map as a function (i.e. without new) should throw an error:

Map is not intended to be called as a function and will throw an exception when called in that manner.

ECMA-262 ed 6 §23.1.1

Original answer for posterity.

Given that:

if (typeof Map != 'undefined') {
  var x = Map();
  var y = new Map();

  // x and y have same [[Prototype]]
  console.log(Object.getPrototypeOf(x) === Object.getPrototypeOf(y)); // true

  // x and y have same constructor
  console.log(x.constructor === y.constructor); // true

  // x [[Prototype]] is Map.prototype
  console.log(Object.getPrototypeOf(x) === Map.prototype); // true

  // x.constructor is Map
  console.log(x.constructor === Map); // true
}

returns true in browsers that support Map, it would seem both return an instance of Map, though the specification could have put that beyond doubt with much simpler language.

It seems the difference is evident when arguments are supplied: when called as a function, the argument should be an iterable object, whereas if called as a constructor, multiple arguments can be provided and the internal [[Construct]] method is called.

Edit

Given that ES6 is still in draft (and may be for some time) this behaviour may change. At the 25 July 2013 TC39 meeting it was discussed that allowing constructors to be behave like constructors even when called without new (Anti–pattern to call a constructor without new)was not a good idea, so it may well be modified in future.

RobG
  • 142,382
  • 31
  • 172
  • 209