Proposal: allow primitives to be explicitly returned from constructors

# Isiah Meadows (7 years ago)

Here's my proposal:

In constructors, currently, non-objects are replaced with this. Here's what I think it should be replaced with:

  1. When calling the constructor, if undefined is returned and new.target is not undefined, return this instead. This is for compatibility and ease of implementation.
  2. When calling the constructor, if anything else is returned, return that value in raw form.

I know this is very likely very breaking, but I wonder if it would be possible.

In case you're curious what this would change in the spec, it would change section 9.2.2, step 13.a-13.c to this:

a. If result.[[Value]] is not undefined, return NormalCompletion(result.[[Value]]). b. If kind is "base", return NormalCompletion(thisArgument).


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Oriol _ (7 years ago)

This would break one of my most preferred ways to check whether a value is an object:

function isObject(value) {
  return value === new function(){ return value };
}

Unlike Object(value) === value, the above is just syntactic, it does not require Object to be the built-in one.

And I don't really see the point, constructors are supposed to construct objects. If you want primitives, you can always use function calls.

# Isiah Meadows (7 years ago)

You already can't assume the result is typeof new function () { return value } === "object" for every value - you can return functions out of them. Also, you could migrate that to avoid creating any temporary objects at all - here's what I usually do:

function isObject(value) {
    return value != null && (
        typeof value === "object" ||
        typeof value === "function"
    )
}

But my idea was just to bring new and normal calls a little closer to one another. Eventually, I'd like to see if primitive wrapper types could disappear, and this is one of the areas where I'm trying to see if it's feasible to do in JS. (Wrapper types are actually a complicating factor in JS optimization and the object model in general.)

But of course, this could all break the web, and if you have no choice in the matter (say, legacy code base with way too many instances of it), it would be too breaking to even be worth doing this.


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Oriol _ (7 years ago)

You already can't assume the result is typeof new function () { return value } === "object"

Yes, that's why I need reliable ways to test whether a value is an object, and your proposal breaks one of these.

here's what I usually do:

function isObject(value) {
    return value != null && (
        typeof value === "object" ||
        typeof value === "function"
    )
}

No, typeof is not reliable, because it's implementation-defined for non-standard non-callable exotic objects.

For example, old IE used to return "unknown" in various cases.

# T.J. Crowder (7 years ago)

On Fri, Apr 20, 2018 at 2:23 PM, Oriol _ <oriol-bugzilla at hotmail.com> wrote:

No, typeof is not reliable, because it's implementation-defined for non-standard non-callable exotic objects.

For example, old IE used to return "unknown" in various cases.

Also "object" for host-provided functions (such as document.createElement); IE8 still does that. (Thankfully IE11 doesn't.) (I suppose that would have passed Isiah's isObject test anyway, but the point is that typeof is, sadly, a weak reed...)

-- T.J. Crowder

# Naveen Chawla (7 years ago)

What's the use case? Maybe there's a nice way of doing what you want

# Isiah Meadows (7 years ago)

I normally name it isReferenceType or similar, but was just reusing the name originally used here. For the purposes of this, my isReferenceType is equivalent to the value === new function () { return value } check, while still avoiding diving into builtins. I was just focused on a correct equivalent.

I know typeof can be rather loose at times with old IE (and heck, it once was in other engines, too - consider the old V8 bug typeof /foo/ === "function").

My question was whether it was feasible now, and specifically with respect to non-primitives (where isReferenceType(value) === false). So far, the only problem explained here was with a fairly unique use case I offered an alternative of, and I'm just trying to explore to see if the breaking nature has changed enough it's worthy to consider lifting the restriction.


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Isiah Meadows (7 years ago)

Okay, you could invert the condition and do it this way. If it's spec-conforming, this should work:

function isObject(value) {
    return value != null &&
        typeof value !== "boolean" &&
        typeof value !== "number" &&
        typeof value !== "string" &&
        typeof value !== "symbol"
}

Alternative, if you're okay with builtins (and this is what I initially had before I sent the original message), you could do this:

var toObject = Object
function isObject(value) {
    return value === toObject(value)
}

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Sultan (7 years ago)

One of the use case for this is – given a function that is either one that returns an instance or one that returns an explicit return value, but would otherwise throw if invoked without the "new" keyword.

Always being able to safely invoke it with "new" is a nice guarantee to have that wouldn't require you to explicitly know before hand whether a function is a constructor or function.

Now since this does not reflect well on the variants that return primitives it means you cannot currently always use "new".

Broadly put this could touch on every use case thats involves the need to know whether a function is a constructor or not before you decide to call it with "new" or not because not calling it with "new" might throw an error.

# T.J. Crowder (7 years ago)

On Thu, Apr 19, 2018 at 11:49 PM, Isiah Meadows <isiahmeadows at gmail.com>

wrote:

Here's my proposal:

In constructors, currently, non-objects are replaced with this. Here's what I think it should be replaced with:

  1. When calling the constructor, if undefined is returned and new.target is not undefined, return this instead. This is for compatibility and ease of implementation.
  2. When calling the constructor, if anything else is returned, return that value in raw form.

I think you'll struggle to demonstrate that this is web-safe; certainly doing so would be a major undertaking.

One particular habit that would be of concern is people writing:

if (condition) return someFunction();

instead of

if (condition) {
    someFunction();
    return;
}

...when they know full well that they don't actually want to return the result of someFunction. Mostly I see that in Node-style callbacks where the return value is ignored, but it wouldn't surprise me at all if there was code in the wild that did that in a constructor, happily (and probably unintentaionally) taking advantage of the fact that the return value will be ignored.

Horribly contrived example:

class Multiplier {
    constructor(a, b) {
    if (typeof a === "undefined") {
      this.setA(7);
      return this.setB(6);
    }
    this.setA(a);
    this.setB(b);
  }
  setA(value) {
    return this.a = value;
  }
  setB(value) {
    return this.b = value;
  }
  execute() {
    return this.a * this.b;
  }
}
const m = new Multiplier();
console.log(m.execute());

And just generally, changing a behavior that's so well-established without some kind of mode (and I'm told There Will Be No More Modes) seems like asking for trouble. Would need to have a massive, unambiguous benefit.

-- T.J. Crowder

# Michael Theriot (7 years ago)

I don't understand the use case. If anything I would like it if returning an object that fails new Class instanceof Class to also fail, not permit even more oddities.

# Isiah Meadows (7 years ago)

My rationale was detailed in a response to another person eariler:

But my idea was just to bring new and normal calls a little closer

to one another. Eventually, I'd like to see if primitive wrapper types could disappear, and this is one of the areas where I'm trying to see if it's feasible to do in JS. (Wrapper types are actually a complicating factor in JS optimization and the object model in general.)

From a reply that (I presume accidentally) missed the list, It doesn't

appear to be web-compatible, so this entire discussion would qualify as moot:

www.chromestatus.com/metrics/feature/timeline/popularity/2054


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Isiah Meadows (7 years ago)

Email missed the list.


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

---------- Forwarded message ---------- From: Isiah Meadows <isiahmeadows at gmail.com>

Date: Fri, Apr 20, 2018 at 12:47 PM Subject: Re: Proposal: allow primitives to be explicitly returned from constructors To: Sathya Gunasekaran <gsathya at chromium.org>

So I guess this has no chance of making it, then... :-(