3

Note: I am not looking for opinions. A single, objective useful example of a primitive constructor would satisfy the question.


Are there any good reasons to use new Number(n)? Why does this constructor exist?

I can think of several reasons to avoid it, eg.,

  • literals are more memory efficient since they don't construct anything new.
  • literals are equal to each other (5 === 5 but new Number(5) !== new Number(5)).
  • the typeof operator indicates that it's a number/str/etc rather than an object.
  • new Number(0) is never falsey. (!!new Number(false) == true).
  • You can use it for type conversion, but there are more concise methods. (+false === 0).

To be clear, I understand the Number object (or String or Object or Array objects) has useful methods and properties, my question is specifically about the constructor.

I am also aware that the constructors are called implicitly when using the methods of these primitive types (eg. 123.45.toFixed(n)), but that doesn't explain why the constructors are exposed to the browser, or how (or if) the constructors themselves are useful.

I'm using Number as an example, but I don't understand why Javascript exposes other constructors where literals are available either (String, Object, Array, etc.).

Why do these exist and are there ever any good reason to use them?

I wrestled a bear once.
  • 22,983
  • 19
  • 69
  • 116
  • For one, might be run times: https://stackoverflow.com/questions/4859800/should-i-be-using-object-literals-or-constructor-functions But I admit I can't think of a good reasoning wrt numbers – Lior Pollak Sep 16 '21 at 15:57
  • 3
    We can't really answer questions about the design rationale of languages here, but for things like this it's usually simply "Why not expose it?" It just falls out of the general design of the type hierarchy, and treating these as special cases would be more work.' – Barmar Sep 16 '21 at 16:00
  • @LiorPollak - I think that question is more about user-defined constructors rather than built-in primitive constructors :/ – I wrestled a bear once. Sep 16 '21 at 16:01
  • @Barmar - My inference from your comment is that they're not really useful at all. Is that your position? If one is asking *why not*, there must not be a good answer to *why*. – I wrestled a bear once. Sep 16 '21 at 16:05
  • I haven't really given it much thought. There may be some obscure needs for it, but I can't think of them. – Barmar Sep 16 '21 at 16:06
  • That's another common language design philosophy -- we may not be able to think of a good use now, but someone else might, and there's no need to prohibit it. – Barmar Sep 16 '21 at 16:07
  • 1
    Quite honestly, some of it may come down to old legacy stuff that we don't use anymore. All the old cruft in JS from its rushed beginnings are why books like [JavaScript: The Good Parts](https://www.oreilly.com/library/view/javascript-the-good/9780596517748/) were important. For example, JS also has a [`with`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with) statement that I've _never_ used and MDN suggests against. FWIW, I've been doing JS dev for 10 years and have probably used a primitive constructor less than 5 times in that span. – Alexander Nied Sep 16 '21 at 16:08
  • Some answers here : https://stackoverflow.com/questions/5750656/whats-the-point-of-new-stringx-in-javascript – Guillaume Georges Sep 16 '21 at 16:16
  • If not, then what would `Object(1).constructor` have to return? Or `1..constructor`? – trincot Sep 16 '21 at 16:27
  • Have a look at [Why does JavaScript have boxed versions of primitives?](https://stackoverflow.com/q/60960197/1048572) – Bergi Sep 16 '21 at 18:22
  • 1
    "*You can use it for type conversion*" - no, you can't use the constructor for that. You would want to call `Number` as a function (without `new`). – Bergi Sep 16 '21 at 18:23

3 Answers3

2

In the end, such a question is answered by "because it is in the specification", but still there is some intuitive logic to have a Number constructor:

As you already mentioned in the question, there is a (useful) prototype object for numbers, so that you can call methods like .toFixed et al. Every JavaScript object with a non-null prototype has a constructor. Constructor and prototype go hand in hand; unless code overwrites the involved properties, there is this relationship for every object that has a non-null prototype:

instance.constructor.prototype === Object.getPrototypeOf(instance)

As this is like a general rule, it makes sense to not deviate from that for (object-wrapped) numbers, and have a meaningful value for that constructor property, i.e. a ... constructor. And by definition of "constructor", it can be called with new.

Useful?

Here we really get into the grey zone of opinion.

Just one observation: Number instances can have custom properties, but even for that purpose you can rely on the automatic wrapping:

let n = Object.assign(2, { isPrime: true });
console.log(n instanceof Number); // true
console.log(n.isPrime); // true
console.log(n*2); // 4
trincot
  • 317,000
  • 35
  • 244
  • 286
2

These constructors create a "Wrapper object" around the primitive type, hence the returned object is fundamentally different than the primitive itself.

var example = new String('abc')

would return a wrapper object that contains the value "abs" and to access it you need to call the function .valueOf()

example.valueOf() // returns abc 

The primitives also have access to the prototype of the wrapper, but still they're fundamentally different. Primitives merely "borrow" the methods on it's prototype from it's wrapper.

The reason of the existence of the primitive's wrapper is merely historical as this behavior exists in Java which Javascript inherited the difference between objects and primitive from.

The only reason I can think of to use it is to add a property on a primitive like the following:

if you do this:

var example = "Foo" ; 
example.bar = "Baz"; // The property "bar" only exist on this line and does not persist; 

Using the wrapper :

var example = new String("foo") ; 
example.bar = "baz" ; // Now you can access the property "baz"
example.charAt(1); //You still can access the methods on the string prototype  

It's very awkward syntax but it works.

Thanks for the great question and I'm looking forward to see all the answers and different discussions to try to understand the existence behind some of the very weird behaviors in Javascript.

Islam Yahia
  • 99
  • 1
  • 5
  • I just want to add that using a primitive constructor to add a property is dangerous. This is because it's easy to lose the object's state as a primitive constructor object and not realize it. For example, in the last code snippet above, If I added `example += ' fighters'` right before `example.bar = "baz"`, it would no longer recognize the bar property. This is because without realizing it, just by using the variable in a normal way, you have reassigned it as a standard string instead of your primitive constructor string object. – dallin Oct 18 '22 at 04:53
1

Are there any good reasons to use new Number(n)?

I can think of one reason but it might not be valid today. It's a stretch, but I believe it could be used for performance reasons depending on the implementation.

The spec says this about a case where you'd do something like:

1.2.toString()

The object that may be created in step 4.a is not accessible outside of the above abstract operation and the ordinary object [[Get]] internal method. An implementation might choose to avoid the actual creation of the object.

So technically, someone that's doing something like that a lot could benefit a little bit if they preemptively created the number object and called the method there. That's if the implementation does not avoid the object wrapper.

However, given current implementations today you would probably have the opposite results and get worse performance.

Anyway, it's a big stretch.

Why does this constructor exist?

Well, a couple of reasons I can think of, mostly because of consistency. First of all, String, Number and Boolean each double down as constructors and as regular functions. They each can convert their input into their respective types when called without new. There were actually very few functions back then that weren't constructors (such as eval or Function.prototype). You also had to think where to put these object prototypes and a pretty obvious spot to put them would have been on these functions, just like the other builtins.

So all in all, you probably wouldn't want these functions to not be constructors because you wouldn't want to put an unnecessary burden of remembering all these edge cases on your users and those who will implement these. We would have questions today like: "Why is Number a function and not a constructor when it even has the Number prototype in its .prototype property?"

I'm using Number as an example, but I don't understand why Javascript exposes other constructors where literals are available either (String, Object, Array, etc.).

Well, each of these can also be extended through class. Extending Array for example was somewhat sought out before ES6. E.g.

class myArrayClass extends Array {
    myCustomMethod(){

    }
}

You also can extend String and Number in a somewhat safe manner.

MinusFour
  • 13,913
  • 3
  • 30
  • 39