Javascript

Whats happening in this code with Number objects holding properties and incrementing the number

19 September 2026 · 10 min read

Whats happening in this code with Number objects holding properties and incrementing the number

Understanding what’s happening in code when you’re dealing with Number objects holding properties and incrementing them can be tricky, especially for those new to JavaScript or similar languages. Unlike primitive number types, Number objects behave differently. When you attempt to add properties to a primitive number, the changes are often ignored or discarded. This article will delve into the nuances of Number objects, how they differ from primitive numbers, and why incrementing properties attached to them might not always produce the results you expect. We’ll explore the underlying mechanisms and provide practical examples to clarify this concept, ensuring you have a solid grasp of how numbers and objects interact in your code. This is particularly important when writing performant and predictable Javascript code.

Understanding Primitive Numbers vs. Number Objects

In JavaScript, there’s a significant distinction between primitive numbers (like 5, 3.14, or -10) and Number objects (created using new Number(5)). Primitive numbers are immutable, meaning you can’t directly modify their value. When you perform operations on them, you’re creating new values. On the other hand, Number objects are instances of the Number constructor, inheriting properties and methods from the Number prototype. While you can add properties to Number objects, this doesn’t fundamentally change the underlying numeric value they represent. Think of it like attaching stickers to a toy car – the car itself remains the same, even with the stickers.

The key difference lies in how the JavaScript engine treats them. Primitive numbers are stored and manipulated directly in memory, optimized for performance. In contrast, Number objects are treated as full-fledged objects, incurring the overhead associated with object creation and management. This distinction becomes critical when you start assigning properties to these numbers, as the behavior will differ markedly. For example, try assigning a property to a primitive number. It won’t stick around in the same way it would with a Number object.

Consider this scenario: you’re building a simple counter. If you use a primitive number to store the count, incrementing it is straightforward: count = count + 1. However, if you mistakenly use a Number object and try to add properties related to the count (like timestamps), you might find that these properties don’t behave as expected when you increment the core numeric value. This is because the engine might be creating a new Number object during the increment, losing the previously attached properties. Understanding this behavior is crucial for avoiding unexpected bugs in your code. According to Mozilla’s documentation, “Primitive values are values that are not objects and have no methods” MDN Web Docs.

The Peculiar Behavior of Adding Properties to Numbers

When you attempt to add properties to a primitive number in JavaScript, the engine temporarily coerces the primitive into an object to allow the property assignment. However, this object is short-lived and discarded almost immediately. Consequently, the property appears to vanish. This behavior can be confusing, especially for developers coming from languages where numbers are inherently objects. It’s a quirk of JavaScript’s type system designed for performance reasons. The featured snippet below clarifies how temporary objects are used in this process.

Featured Snippet: When you try to assign a property to a primitive number (like 5.myProperty = “hello”), JavaScript creates a temporary Number object, assigns the property to it, and then immediately discards this object. Therefore, the property doesn’t “stick” to the original number, making it seem like the assignment had no effect. This is different from assigning properties to Number objects created with new Number(), which persist as expected.

Let’s illustrate with code. Try running let num = 5; num.property = “value”; console.log(num.property);. The output will be undefined, demonstrating that the property assignment failed. Conversely, if you use a Number object (let numObj = new Number(5); numObj.property = “value”; console.log(numObj.property);), the output will be “value”. This highlights the fundamental difference in how JavaScript handles property assignments to primitive numbers versus Number objects. This behavior directly impacts how you should design your code, especially when dealing with data that needs to be associated with numeric values. According to a Stack Overflow discussion, this behavior is a common source of confusion for JavaScript developers Stack Overflow.

Incrementing and Property Persistence: A Deep Dive

Incrementing a Number object that has properties can lead to unexpected results if you’re not careful. The increment operation (++) might create a new Number object, effectively discarding any properties you previously attached. This is because incrementing a Number object involves retrieving its numeric value, adding one to it, and then creating a new Number object with the updated value. The original object, with its properties, is essentially replaced. Here’s how you can avoid this pitfall:

  1. Avoid using Number objects for simple numeric operations: Stick to primitive numbers whenever possible for basic arithmetic.
  2. If you need to associate properties with a number, consider using a plain JavaScript object: Instead of new Number(5), use { value: 5, property: “value” }.
  3. If you must use Number objects, be aware of the potential for property loss during increment operations: Reassign properties after each increment if necessary.

To illustrate, consider this code snippet: let numObj = new Number(5); numObj.property = “value”; numObj++; console.log(numObj.property);. The output will likely be undefined because the increment operation replaced the original object with a new one. To preserve the property, you would need to reassign it after the increment: numObj++; numObj.property = “value”;. This extra step is a consequence of using Number objects and should be carefully considered in your code design. Remember to always test thoroughly when working with Number objects to ensure your code behaves as expected.

Best Practices and Alternative Approaches

Given the complexities and potential pitfalls of using Number objects holding properties, it’s often better to adopt alternative approaches. The most common and recommended practice is to use plain JavaScript objects to associate data with numeric values. This provides a more straightforward and predictable way to manage both the numeric value and its associated properties. This practice avoids the problems inherent in the Number object wrapper class.

Here are some best practices to keep in mind:

  • Use primitive numbers for arithmetic: Stick to let count = 5; instead of let count = new Number(5); for basic calculations.
  • Use plain JavaScript objects for data association: If you need to store properties alongside a number, use an object like { value: 5, label: “Item Count” }.

For example, instead of:

javascript let numObj = new Number(5); numObj.label = “Item Count”; numObj++; console.log(numObj.label); // undefined Use:

javascript let data = { value: 5, label: “Item Count” }; data.value++; console.log(data.label); // “Item Count” This approach is cleaner, more efficient, and avoids the unexpected behavior associated with Number objects. Additionally, consider using classes or data structures if you need more complex behavior. For instance, you might define a Counter class that encapsulates the numeric value and any associated properties, providing methods for incrementing and managing the data. According to a performance benchmark on JSPerf, using primitive numbers for arithmetic operations is significantly faster than using Number objects JSPerf. Choosing the right approach can lead to more robust and maintainable code. You can also explore other Javascript object types.

Infographic here
FAQ: Number Objects and Property Persistence --------------------------------------------
Why do properties disappear when I increment a Number object?
Incrementing a **Number object** often creates a new **Number object** with the incremented value, discarding any properties previously assigned to the original object.
Can I reliably add properties to primitive numbers in JavaScript?
No, attempting to add properties to primitive numbers results in temporary object creation, and the properties are not retained.
What's the best way to associate data with numeric values in JavaScript?
Using plain JavaScript objects (e.g., { value: 5, label: "My Value" }) is generally the best and most reliable approach.
It's clear that working with **Number objects holding properties** and incrementing them requires careful consideration. The seemingly simple act of adding a property or incrementing a value can lead to unexpected behavior if you're not aware of the underlying mechanisms. By understanding the difference between primitive numbers and **Number objects**, and by adopting best practices such as using plain JavaScript objects, you can avoid common pitfalls and write more predictable and maintainable code. If you're still unsure, experiment with the code snippets provided and see the behavior firsthand. Explore other Javascript quirks to deepen your knowledge. **Question & Answer :** [A recent tweet](https://twitter.com/weitzelb/status/718623065480019968) contained this snippet of JavaScript.

Can someone please explain what is happening in it step by step?

> function dis() { return this } undefined > five = dis.call(5) Number {[[PrimitiveValue]]: 5} > five.wtf = 'potato' "potato" > five.wtf "potato" > five * 5 25 > five.wtf "potato" > five++ 5 > five.wtf undefined > five.wtf = 'potato?' "potato?" > five.wtf undefined > five 6 

In particular, it is not clear to me:

  • why the result of dis.call(5) is a Number with some kind of a [[PrimitiveValue]] property, but the results of five++ and five * 5 appear to just be the plain numbers 5 and 25 (not Numbers)
  • why the five.wtf property disappears after the five++ increment
  • why the five.wtf property is no longer even settable after the five++ increment, despite the five.wtf = 'potato?' assignment apparently setting the value.

OP here. Funny to see this on Stack Overflow :)

Before stepping through the behaviour, its important to clarify a few things:

  1. Number value and Number object (a = 3 vs a = new Number(3)) are very different. One is a primitive, the other is an object. You cannot assign attributes to primitives, but you can to objects.

  2. Coercion between the two is implicit.

    For example:

    (new Number(3) === 3) // returns false (new Number(3) == 3) // returns true, as the '==' operator coerces (+new Number(3) === 3) // returns true, as the '+' operator coerces 
    
  3. Every Expression has a return value. When the REPL reads and executes an expression, this is what it displays. The return values often don’t mean what you think and imply things that just aren’t true.

Ok, here we go.

Original image of the JavaScript code

The pledge.

> function dis() { return this } undefined > five = dis.call(5) [Number: 5] 

Define a function dis and call it with 5. This will execute the function with 5 as the context (this). Here it is coerced from a Number value to a Number object. It is very important to note that were we in strict mode this would not have happened.

> five.wtf = 'potato' 'potato' > five.wtf 'potato' 

Now we set the attribute five.wtf to 'potato', and with five as an object, sure enough it accepts the Simple Assignment.

> five * 5 25 > five.wtf 'potato' 

With five as an object, I ensure it can still perform simple arithmetic operations. It can. Do its attributes still stick? Yes.

The turn.

> five++ 5 > five.wtf undefined 

Now we check five++. The trick with postfix increment is that the entire expression will evaluate against the original value and then increment the value. It looks like five is still five, but really the expression evaluated to five, then set five to 6.

Not only did five get set to 6, but it was coerced back into a Number value, and all attributes are lost. Since primitives cannot hold attributes, five.wtf is undefined.

> five.wtf = 'potato?' 'potato?' > five.wtf undefined 

I again attempt to reassign an attribute wtf to five. The return value implies it sticks, but it in fact does not because five is a Number value, not a Number object. The expression evaluates to 'potato?', but when we check we see it was not assigned.

The prestige.

> five 6 

Ever since the postfix increment, five has been 6.