Default @@toStringTag for user classes

# Dmitry Soshnikov (4 years ago)

Probably worth providing a default implementation of the @@toStringTag when evaluating a class [1]. In this case users will be able to do:

class Point { ... }

var p = new Point(1, 2);
console.log(p); // "[object Point]"

The default implementation will be just (if the className is defined of course):

proto[@@toStringTag] = function() {
  return className;
};

[1] people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-classdefinitionevaluation

Dmitry

# Michał Wadas (4 years ago)

It probably would be backward incompatible change. Too much code depends on [Object Object]. 2 gru 2014 08:46 "Dmitry Soshnikov" <dmitry.soshnikov at gmail.com> napisał(a):

# Claude Pache (4 years ago)

Le 2 déc. 2014 à 09:04, Michał Wadas <michalwadas at gmail.com> a écrit :

It probably would be backward incompatible change. Too much code depends on [Object Object].

I'm curious to know what sort of code would be broken by O.p.toString.call(x) === "[object Point]" for instances x of some user-defined class? and whether it is not already broken by, say, O.p.toString.call(new Set) === "[object Set]"?

# Andrea Giammarchi (4 years ago)

Same question here.

AFAIK usually the ({}.toString.call(generic) === "[object Object]") check is the default: in a switch, the last else in a flow, etc etc ... although I wouldn't be surprised if some code, somewhere, would do strict comparison to know if it's a user defined object or not. In that case there will be problems.

To be honest I also don't see much advantage in having this configurable when obj.constructor.displayName or obj.constructor.name can be checked instead and eventually fallback into Object.prototype.toString.call(obj)

This will work backward compatible too

br

# Claude Pache (4 years ago)

Le 2 déc. 2014 à 08:46, Dmitry Soshnikov <dmitry.soshnikov at gmail.com> a écrit :

Hi,

Probably worth providing a default implementation of the @@toStringTag when evaluating a class [1]. In this case users will be able to do:

class Point { ... }

var p = new Point(1, 2);
console.log(p); // "[object Point]"

You seem to imply that console.log(p) will show the result of p.toString() in the console. But it is not the case for the majority of browsers.

I've just tried:

var Point = function() {}
Point.prototype.toString = function() { return "(this is an object of type Point)" }
console.log(new Point)

Results are:

Firefox: Object { } Chrome: Point{toString: function} Safari: Object IE: [object Object] (this is an object of type Point)

In particular, note that Chrome doesn't need the help of .toString() in order to log useful information.

# Michał Wadas (4 years ago)

The difference between "[Object Point]" and "[Object Set]" is fundamental. Your application would not change any behavior without explicit creating "new Set". But changing behavior of existing code is something different - it can introduce subtle bugs in enclosed environment. 2 gru 2014 13:03 "Claude Pache" <claude.pache at gmail.com> napisał(a):

# Andrea Giammarchi (4 years ago)

I think by @@toStringTag he meant the ability to define a [[Class]] name so that {}.toString.call(generic) would return such name instead of Object but I'm sure Dmitry will come back explaining and/or asking more.

# Domenic Denicola (4 years ago)

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Andrea Giammarchi

I think by @@toStringTag he meant the ability to define a [[Class]] name so that {}.toString.call(generic) would return such name instead of Object but I'm sure Dmitry will come back explaining and/or asking more.

[[Class]] does not appear in the spec, and @@toStringTag does.

# Domenic Denicola (4 years ago)

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Michal Wadas

But changing behavior of existing code is something different - it can introduce subtle bugs in enclosed environment.

Nobody is proposing changing the behavior of existing code. They are proposing changing the behavior of class syntax, and there is no existing code that uses class syntax.

# Dmitry Soshnikov (4 years ago)

On Tue, Dec 2, 2014 at 9:56 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

I think by @@toStringTag he meant the ability to define a [[Class]] name so that {}.toString.call(generic) would return such name instead of Object but I'm sure Dmitry will come back explaining and/or asking more.

Yeah, so basically current O.p.toString [1] in step 14 delegates to the @@toStringTag. Which means user-classes have ability to ad-hoc the result of using default toString from O.p.

In a class users have to put it manually at the moment:

class Point {
  constructor(x, y) {
    this._x = x;
    this._y = y;
  }

  toString() {
    return '<Point ' + this._x + ', ' + this._y + '>';
  }

  [Symbol.toStringTag]() {
    return 'Point';
  }
}

var p = new Point(10, 20);

console.log(p.toString()); // '<Point 10, 20>'

console.log(({}).toString.call(p)); // '[object Point]'

Notice how the implementation defines its own toString, and at the same time provides the ability to test the type tag with the O.p.toString.

So my proposal is to provide default implicit implementation of that Symbol.toStringTag method (which is @@toStringTag in the spec).

Dmitry

[1] people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring

# Dmitry Soshnikov (4 years ago)

On Tue, Dec 2, 2014 at 5:12 PM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com

wrote:

On Tue, Dec 2, 2014 at 9:56 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

I think by @@toStringTag he meant the ability to define a [[Class]] name so that {}.toString.call(generic) would return such name instead of Object but I'm sure Dmitry will come back explaining and/or asking more.

Yeah, so basically current O.p.toString [1] in step 14 delegates to the @@toStringTag. Which means user-classes have ability to ad-hoc the result of using default toString from O.p.

In a class users have to put it manually at the moment:

class Point {
  constructor(x, y) {
    this._x = x;
    this._y = y;
  }

  toString() {
    return '<Point ' + this._x + ', ' + this._y + '>';
  }

  [Symbol.toStringTag]() {
    return 'Point';
  }

This one seems should be a getter actually based on the algorithm of O.p.toString:

get [Symbol.toStringTag]() {
  return 'Point';
}

Dmitry