Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What makes new Date() / 1000 a valid javascript?

Tags:

javascript

How come I can "divide" an object by a number?

like image 918
mark Avatar asked Dec 09 '22 22:12

mark


2 Answers

Late update
Given that my answer contained some incomplete and -at times- outright wrong information, I thought it best to correct my mistakes. Albeit late, here goes:

Almost All JS objects have 23 methods in common: valueOf() in case of the date object, it maps back to the getTime() method, which returns a number. What this number is, is the number of milliseconds since jan 1st 1970, Many programming languages use this date to base their dates on. It's referred to as the Unix Timestamp, the date is called the Unix Epoch. in case you were wondering.
Another method is toString, which returns a string (obviously).
The third method is hasOwnProperty, which can be traced back in the prototype-chain to Object.prototype, but that's not important for this question.

Whenever you compare 2 values (objects or not), JS will coerce the types of both operands in such a way that the two values can be safely compared (1 == '1' coerces the string '1' to a Number, for example).
This type-coercion also applies when an object or primitive value is concatenated into a stirng, or when an expression is evaluated to a singular value (eg in your console, type new Date;, and you'll see what I mean).
The default behaviour of the Date object is to be coerced to a string, the toString method is invoked.

So, the Date instances are no different. They are objects, which means the variables' values will be references to objects. However in your expression/statement, you're using an arithmetic operator (/) on the object. A string wouldn't make much sense in this context, so JS falls back to the valueOf method (sort of, Date's can be coerced to numbers in a variety of ways). This does yield a Number, which can be divided effortlessly.
The same behaviour can be expected (and can indeed be observed) when using the comparison operators >, <, <= and >=.
This means that comparing dates, and using them to generate output is, most of the time, a doddle:

var d1 = new Date();
var d2 = new Date(1234567890123);
if (d1 > d2)
{
   //do stuff
}

When written in full, this is:

Date.prototype.valueOf = Date.prototype.getTime; //<-- this isn't how its done, but think of the valueOf method as doing something similar

if (d1.getTime() > d2.getTime()) //OR:
if (d1.valueOf() > d2.valueOf())

There are other advantages, too, apart from comparison:

var d3 = new Date(d1 - 1000); //new date, 1 second before d1

But there is a trade-off/pitfall:
As with all objects in JS, equality checks using == or === is a bit odd at first:

var a = new Date();
var b = new Date(a.valueOf());
console.log(a.toString() === b.toString());//true
console.log(a.valueOf() === b.valueOf());//true, to the milisecond
console.log(a == b);//false!
console.log(a === b);//false!!

Like I said: variables that are assigned objects don't actually hold the value of that object, they reference it. Since both a and b reference a different instance of the same object, they are not equal. Also because they're both objects (of the same type, to that) there is no type coercion going on.
Think of it as 2 houses, 100% equal in every way, except for the address. If there is no coercion, you're actually saying something like:
"There's a house on Fiction street 1, and on Fiction Street 2. Are these two houses equal?" To which the answer is a resounding no.

To fix this, you'll have to explicitly ask JS to compare the way these houses look by coercing them to another type manually. This is a lot easier than it sounds:

console.log(a.valueOf() == b.valueOf());//true
//or shorter, still:
console.log(+(a) == +(b));
//coerce to strings, either call toString, or:
console.log((''+a) == (''+b));

Again this may seem silly at first, but this way, you can at least test if two variables actually do reference the same instance. Suppose we have 2 objects taking up memory, when we really only need 1, we can free the reference to one of these instances to be GC'ed:

if (a !== b && +(a) === +(b))
{//their values are the same, but different instances
    b = a;//now b references the same object as a
}
console.log(a === b);//true
console.log(+(a) === +(b));// true again

Now there still are a few oddities, mind you, that affect the Date object in particular. Try to guess what the following statements would log:

a.setTime(a.getTime() + 1000);//add 1 second
console.log(a == (b + 1000));
console.log(a === (b + 1000));
console.log((a-1000) == b);
console.log((a-1000) === +b);

The answer is: false, false, false and true. How come?
The first case is simple: the + operator is overloaded, it also concatenates strings, and the Date object's default behaviour is to toString itself, so b+1000 is evaluated as b.toString() + '1000'.
Next, using the type and value checking === will almost always be false, since no coercion is going on.
Then, by subtracting 1000 using the not overloaded - arithmetic operator means that the left operand will still be evaluated to a number. The right operand, however will still revert to its befault behaviour of being coerced to a string. They are not equal.
The last example explicitly coerces the right operand to a number, and yields true.

To get true in all of the above cases, here's what to write

console.log(+a == (+b + 1000));
console.log(+a === (+b + 1000));
console.log((a-1000) == +b);
console.log((a-1000) === +b);

Simply explicitly coerce your Date instances and you should be OK

like image 134
Elias Van Ootegem Avatar answered Dec 25 '22 18:12

Elias Van Ootegem


When you execute an arithmetic operation on an object, its valueOf function is implicitly called.

Try the following to have an object that you can actually divide:

function Foo() {

    this.valueOf = function() {
        return 500;
        };

    }

var bar = new Foo();
console.log(bar/1000); // -> 0.5
like image 43
Amberlamps Avatar answered Dec 25 '22 19:12

Amberlamps