52

I am following a JavaScript tutorial on W3Schools. While reading almost on each page they give note to user to "Avoid creating objects" and to use primitive data types instead. They give reason for this as "code becomes difficult to understand or execution speed will be decreased if object are used". Is it true that we should avoid creating objects in JavaScript?

For example:

var value = new Number(1);  // Avoid this
var value = 1;              // do something like this instead.
alejo4373
  • 115
  • 7
WeAreRight
  • 885
  • 9
  • 24
  • 82
    w3schools.com is notoriously poor. For decent information, look elsewhere, such as [MDN](https://developer.mozilla.org/docs/JavaScript/). That said, creating the object versions of primitives is almost never necessary or appropriate. – T.J. Crowder Jan 04 '17 at 07:07
  • 4
    well, in the example you provide, using the constructor-function would be sort of hard to read. But as your code gets more complex, it becomes sort of impossible not to use objects. Also, I find w3schools' information to not be that great. I'd recommend looking at MDN (https://developer.mozilla.org/en-US/) for more in-depth documentation. Although it might be understandable if you're just using w3schools as a starting point. – stinodes Jan 04 '17 at 07:07
  • 5
    @Jone Dotosvky Have a read here http://www.w3fools.com/ – Flying Gambit Jan 04 '17 at 07:10
  • 1
    One of the reasons could be code optimization and readability – Flying Gambit Jan 04 '17 at 07:12
  • 1
    Related: http://stackoverflow.com/questions/21933120/what-is-the-difference-between-javascript-object-and-primitive-types – charlietfl Jan 04 '17 at 07:12
  • Could you point to the relevant page in the tutorial you are following? –  Jan 04 '17 at 07:24
  • @torazaburo There are many but i am pointing few one here, search for " The new keyword complicates the code", it will take you to exact location of note . http://www.w3schools.com/js/js_strings.asp, http://www.w3schools.com/js/js_numbers.asp, http://www.w3schools.com/js/js_booleans.asp – WeAreRight Jan 04 '17 at 08:35
  • Wow, I had forgotten how bad w3schools was. You really ought to find yourself another tutorial. –  Jan 04 '17 at 10:13
  • 1
    possible duplicate of [Why should you not use Number as a constructor?](http://stackoverflow.com/q/369220/1048572), [What is the difference between string literals and String objects in JavaScript?](http://stackoverflow.com/q/17256182/1048572) and more – Bergi Jan 04 '17 at 10:33
  • 3
    @FlyingGambit I bet you didn't even read what's on that site right at this moment. – Malcolm Jan 04 '17 at 16:18
  • 1
    @T.J.Crowder That's just bandwagon hate, they improved a lot over time. – Malcolm Jan 04 '17 at 16:33
  • 1
    @Malcolm: I'm glad they've improved, but I just checked and they still omit things like String#replace placeholders, its ability to call callbacks, etc. I'd still call that poor. (I'd link to it, but they don't seem to support that, either. Here's the page: http://www.w3schools.com/js/js_string_methods.asp Search for "Replacing String Content".) – T.J. Crowder Jan 04 '17 at 16:37
  • @T.J.Crowder Because what you're viewing is a tutorial, not a complete reference. It has different purpose. Which would be the alternative page to that, by the way? – Malcolm Jan 04 '17 at 17:32
  • @Malcolm: Fair enough. So I went back to it, and had to actually search for the word "reference" to find the link to the "complete string reference," then find the [`replace` entry](http://www.w3schools.com/jsref/jsref_replace.asp) (a link from the tutorial would have been good), and guess what? It still doesn't explain replacement string placeholders, and doesn't explain using a function as a callback. (There's one example *using* a function as a callback; no explanation, and no demonstration of capture groups being args.) I think this is a point on which we'll just have to agree to disagree. – T.J. Crowder Jan 04 '17 at 18:43
  • @T.J.Crowder Well, I agree that their reference isn't very complete (never said _their_ reference is) and you agreed that the tutorial is OK. The original question was discussing a tutorial, not the reference. – Malcolm Jan 04 '17 at 19:05
  • @Malcolm: No, I did not agree that the tutorial was okay, my "fair enough" was your pointing out it was a tutorial, not a reference. I'm not continuing this further, it's not worth your time or mine. Happy coding! – T.J. Crowder Jan 05 '17 at 07:16
  • @T.J.Crowder Then the point that a tutorial has a different purpose still stands. – Malcolm Jan 05 '17 at 08:59

4 Answers4

111

The statement "avoid creating objects" on its own is absurd in JavaScript, which has objects everywhere and is one of the most object-oriented languages in existence. But "avoid creating object versions of primitives," which is what the code you quote does, is valid. That is, avoid new String, new Number, and new Boolean.

JavaScript has both primitive and object versions of strings, numbers, and booleans. There's almost never any reason to create the object version of any of them explicitly, and doing so can indeed lead to confusion; see inline comments:

var s1, s2, n1, n2;

// These are false because with ===, an object is never equal to a non-object
s1 = new String("hi");
s2 = "hi";
console.log(s1 === s2); // false
n1 = new Number(42);
n2 = 42;
console.log(n1 === n2); // also false

// These are false because even with ==, two *different* objects are never equal
// (even if they're equivalent)
s1 = new String("what the...");
s2 = new String("what the...");
console.log(s1 == s2);  // also false
n1 = new Number(42);
n2 = new Number(42);
console.log(n1 == n2);  // also false

The object versions of strings, numbers, and booleans largely exist to enable methods on primitives to be provided using the same mechanism that provides methods to object types. When you do

console.log("foo".toUpperCase()); // "FOO"

a temporary object is created for the primitive string "foo", and then the toUpperCase property is read from that object. Since the object inherits from String.prototype, it has toUpperCase and all is well. Once the operation is done, the temporary object is thrown away (unless something keeps a reference to it, but nothing does and nothing can with toUpperCase, you'd have to add a method to String.prototype that returned the object in order for it to be kept around).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Great answer! Another thing that's tangentially related is the behavior of `this` when primitives are passed to a function context in non-strict mode vs. strict mode. Something like this: `function coerce() { return this; }; typeof coerce.call(5) === "object"; /* non-strict mode */ typeof coerce.call(5) === "number"; /* strict mode */` – Patrick Roberts Jan 04 '17 at 08:07
  • I just meant it was related because it's a way of keeping a reference to the temporary object created when doing so in non-strict mode. For example, you could assign the function to a prototype to do that: `Number.prototype.coerce = coerce; 5..coerce();` – Patrick Roberts Jan 04 '17 at 08:31
  • 1
    Given `var q=foo; var r=q.toUpperCase();` is there any reason that a decent implementation would bother creating an object, as opposed to recognizing the pattern and feeding a primitive string to a case-conversion function? – supercat Jan 04 '17 at 15:55
  • 3
    @supercat: None that I'm aware of *if* `String.prototype.toUpperCase` is still the original value the engine gave it at startup. And indeed, I'd be fairly surprised if they didn't (but then, I'm sometimes fairly surprised). – T.J. Crowder Jan 04 '17 at 16:04
14

It changes the intuitive way the operators behave with numbers, strings and booleans:

  • the strict comparison (===) breaks when any of the numbers is constructed, so 42 === 42 is true, while 42 === new Number(42) is not,
  • the abstract comparison (==) breaks when both numbers are objects, so 42 == new Number(42) is true, while new Number(42) == new Number(42) is not,
  • the typeof operator gives different result when a number is constructed, so typeof(42) is number, but typeof(new Number(42)) is object,
  • when converted to a boolean, 0 is false, but new Number(0) is true, so the following two will have different behavior:

var a = 0;
if (a)
  console.log("not zero");
else
  console.log("zero!");     // "zero!"

var b = new Number(0);
if (b)
  console.log("not zero");     // "not zero"
else
  console.log("zero!");

So, avoid new Number, new String and new Boolean.

Apart from that, there is the issue of using / not using new with constructors. It stems from several facts:

  • in JS, a constructor is a regular function, using this.foo syntax to add new properties and methods;
  • when invoked without the new keyword, this becomes the global object, leading to side effects.

As a result, a tiny mistake can have catastrophic effects:

color = "blue";

var Fruit = function(color) {
  this.color = color;
  return this;
};

var apple = new Fruit("green");

console.log(apple.color);       // "green"  --  okay

console.log(color);             // "blue"  --  okay

var banana = Fruit("yellow");

console.log(banana.color);      // "yellow"  --  okay

console.log(color);             // "yellow"  --  wait, what?

console.log(banana.apple);      // "{ color: 'green' }"  --  what??

console.log(banana.document);   // "{ location: [Getter/Setter] }"  --  what???

(That's why some people resort to adding explicit checks in the constructor, or using closures instead. But that's for another story.)

Octavia Togami
  • 4,186
  • 4
  • 31
  • 49
M. M.
  • 322
  • 1
  • 10
  • 1
    Typically, you don't use `return this;` in a constructor. That's redundant, and omitting it helps developers using your constructor to debug faster, since `banana` would be equal to `undefined` instead of `window`. Another thing to consider is that your example assumes non-strict mode instead of strict mode, where the function would throw the error `Cannot set property 'color' of undefined` because no context is defined due to the lack of `new`. – Patrick Roberts Jan 04 '17 at 19:37
  • new operator in JS instinates an empty object => {} and that's why the comparison fails. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new – ALLSYED Jan 05 '17 at 04:02
  • 1
    *"when invoked without the `new` keyword, `this` becomes the global object, leading to side effects."* Only in loose mode, which one shouldn't use. In strict mode (available since ES5), when calling `Fruit()`, `this` within `Fruit` will be `undefined`, so `this.color` would be an error. Also, as of ES2015, if you use `class` syntax, `Foo` will be a class constructor, and calling those without `new` has its own specific error. – T.J. Crowder Jan 11 '17 at 09:03
0

Everybody only say "avoid using", "it can lead to confusion", "typeof x behaves weird", and for the most part they are right. But nobody is able to give you one single reason as to why you would want to use the constructor instead.

When you have many variables that has the same value, then you will allocate much more memory, if you instead construct a new class instances and use that one instead, then you would only allocate 1 single item. and the rest of your variables would just be pointers to the same object.

This could technically increase speed when you use structuredClone (but i don't know, haven't bench tested it)

Something that i have tested out at least is to see how much disc space you allocate when using IndexedDB.

// small simple kv indexeddb storage
let p,query=(e,...r)=>(p??=new Promise((e=>{const r=indexedDB.open("kv");r.onupgradeneeded=()=>r.result.createObjectStore("kv"),r.onsuccess=()=>{const t=r.result;query=(e,...r)=>{const n=t.transaction("kv","readwrite").objectStore("kv")[e](...r);return new Promise(((e,r)=>{n.onsuccess=()=>e(n.result),n.onerror=()=>r(n.error)}))},e()}}))).then((()=>query(e,...r)));
var kv=(...e)=>query(...e);

var arr = Array(1024).fill('a'.repeat(1024))

await kv('put', arr, 'stuff')
await kv('get', 'stuff')

var est = await navigator.storage.estimate()
est.usageDetails.indexedDB // chrome 105 allocated 1055761 bytes

now if we do the same thing but using slightly different thing by using string constructor instead:

// Now you are using the same instance instead.
var arr = Array(1024).fill(new String('a'.repeat(1024)))

await kv('put', arr, 'stuff')
await kv('get', 'stuff')

var est = await navigator.storage.estimate()
est.usageDetails.indexedDB // chrome 105 allocated 7353 bytes

now you saved about 1055761-7353 = 1048408 bytes...

If you want to test this out for yourself, always open up a new inkognito window and await both put/get operators, estimate can maybe give wrong value otherwise. and deleting it may not always clean up properly, that's why you should create a new inkognito window every time you want to compare stuff.

But in the end: yeaa... maybe don't use the constructor after all. it's almost never a good thing.

Just wanted you to know what the "real" differences is by using objects instead

...also if you use NodeJS v8.serialize(value) then the same example will yield a smaller Buffer when you use the same object instances (as the rest will just be pointers)

another reason for using objects instead could be if you wanted to do something with WeakRef, WeakMap where simple literals isn't acceptable.

Endless
  • 34,080
  • 13
  • 108
  • 131
-2

Do not use new when invoking a Number(). Source: JSLint...

The "Do not use {a} as a constructor" error is thrown when JSLint, JSHint or ESLint encounters a call to String, Number, Boolean, Math or JSON preceded by the new operator. (Source: LintErrors.com: Do not use {a} as a constructor

console.log(Number("3") == Number("3"))

console.log(new Number("3") == new Number("3"))
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133