The Date Puzzle And Hidden Properties
How can we subtract two objects from each other to find the time difference? It seems impossible, yet JavaScript has a secret.
We've seen that objects are stored by reference and comparing them only checks their memory addresses. This creates a real problem for user experience. Imagine a user accidentally double-clicks a "Submit" button. We want to check if the second click happened too soon after the first and prevent a double purchase.
The Essentials
- The
DateObject: A built-in object that stores a timestamp of "right now." - Hidden Properties: Objects can have properties that aren't visible or accessible via normal dot notation (like
[[DateValue]]). - The Puzzle: If
time1andtime2are both objects (references), how can we ever dotime2 - time1?
The Double-Click Problem
When a user clicks "Submit," we create a timestamp using new Date(). This returns an object with a hidden property called [[DateValue]], which stores the number of milliseconds since January 1st, 1970.
Let's say 1 second passes between two clicks.
const time1 = new Date(); // Midnight, Jan 15, 2027 (1.8 trillion ms)
const time2 = new Date(); // 1 second later (1.8 trillion + 1000 ms)
if (time2 - time1 < 2000) {
console.log("Accident! Too soon.");
}The Impossible Math
Wait a minute. time2 and time1 are objects. They are stored in the heap at different addresses (like 10002 and 10001). Subtracting one address from another shouldn't give us the number of milliseconds. It shouldn't even be possible.
Yet, it works. JavaScript manages to get under the hood, grab those hidden millisecond values, and perform the subtraction.
Step 1:time1 points to a Date object with a hidden [[DateValue]] of 1.8 trillion.
The Secret: @@toPrimitive
How is this possible? It's via a hidden property called @@toPrimitive.
When JavaScript sees math being done on an object, it doesn't look at the memory position. Instead, it asks: "Do you have a way to convert yourself to a primitive?"
The Date function ensures that every object it returns has a hidden instruction set (a function) stored on this @@toPrimitive property. These instructions tell JavaScript: "When someone tries to treat me like a number, return my hidden [[DateValue]]."
The "Square Bracket" Gotcha
Before we learn how to wield this power ourselves, we need to remember how we access properties. If we have a variable month = "Jan", we can use time1[month] = true to set a property called "Jan."
The thing inside the square brackets is evaluated before it's used as a key. This is the key to metaprogramming. If we can find a way to refer to that hidden @@toPrimitive label, we can add it to our own objects.
But how do we refer to a label that we can't even write out? The answer lies in a new data type: Symbols.
Further Reading and Watching
- Video: The Date Puzzle and Hidden Properties
- MDN: Date Object
- Concept: Symbol.toPrimitive
Keep reading