How does MomentJS work comparing values with > and < (and >=, <=)? - javascript

I'm using moment.js in a current project and I see that the comparison operators <,>,>=,<= work correctly. However == does not.
It surprises me that these work and that you don't need to use the .isBefore() and .isAfter() functions.
Can anyone give me a brief description of why these work? Would it be possible to make == work as well?
Thanks.

This is due to how comparison and equality operators in JavaScript work (emphasis mine):
For relational abstract comparisons (e.g., <=), the operands are first
converted to primitives, then to the same type, before comparison.
[...]
The equality operator converts the operands if they are not of the
same type, then applies strict comparison. If either operand is a
number or a boolean, the operands are converted to numbers if
possible; else if either operand is a string, the string operand is
converted to a number if possible. If both operands are objects, then
JavaScript compares internal references which are equal when operands
refer to the same object in memory.
So when two moment objects are compared with inequalities, they are first converted to numbers. For Moment.js objects, this is milliseconds since the unix epoch of midnight UTC, Jan 1st 1970.
In your favorite browser's console window/node REPL:
> +moment()
<- 1412332710977
For the == equality check, the runtime does a reference comparison between the two objects, which returns false for two different moment instances, even if they both are for the same date/time.
A lack of default .equals() or operator==() overloading in JavaScript makes this behaviour rather counter-intuitive, especially if you're coming from other languages.
Also note that Moment's isBefore/isAfter functions are really slow, as they clone both moment objects internally before doing the comparison (this is because there is an optional argument to isBefore/isAfter to specify which time component to compare on (e.g. 'hour') and it clones regardless of whether this argument is present or not).
Moment suffers here from having mutable objects, which is why it is doing a defensive clone() in the first place, and then further from not having optimised the common path where this is actually unnecessary.

The magic is the ValueOf(). JavaScript calls the valueOf() method to convert an object to a primitive value when comparing. Moment object overrides valueOf() method to basically return the value valueOf() of underlying native Date object, which returns the epoch number.

Related

Understanding type casting of + operator in momentjs

I have a date object in moment
const myDate = moment.utc(new Date()).startOf('day');
Approach # 1
console.log(myDate.valueOf());
vs Approach # 2
console.log(+myDate());
Both of these do the same thing, I want to know how this +myDate() operates & I couldn't find the documentation for this anywhere.
This is not a momentjs thing, it's how the unary + operator works in JavaScript.
The unary plus operator precedes its operand and evaluates to its
operand but attempts to convert it into a number, if it isn't already.
Although unary negation (-) also can convert non-numbers, unary plus
is the fastest and preferred way of converting something into a
number, because it does not perform any other operations on the
number. It can convert string representations of integers and floats,
as well as the non-string values true, false, and null. Integers in
both decimal and hexadecimal ("0x"-prefixed) formats are supported.
Negative numbers are supported (though not for hex). If it cannot
parse a particular value, it will evaluate to NaN.
I couldn't find the documentation for this anywhere.
It's in the JavaScript specification. :-)
The unary + operator converts its operand to number via the abstract ToNumber operation, which (for objects) calls valueOf (then if necessary because that returns an object[!], calls toString) and converts the resulting primitive to a number if it isn't already one.
Since the moment object supports valueOf and it returns a number, +myDate and myDate.valueOf() do the same thing.

What is the domain of the plus operator in JavaScript?

I'm looking at the MDN documentation of the plus (a + b) operator in JavaScript. It seems like the operator is defined on all combinations of number, string, and bool arguments. From experimentation, it is also defined on arrays, objects, null, closures ...
The question: is there any combination of values for which the plus operator is not defined, i.e. those arguments are not in the domain of the + function? (Where operationally, "not defined" basically means an exception will be thrown or the program will halt with an error if + is applied to those arguments.)
Bonus points for answering the equivalent question for the other "arithmetic operators" (which seem to do far more than arithmetic ...).
The + operator is only defined for numbers (addition) and strings (concatenation).
However, it coerces its arguments to number or string. Full details in the spec, §11.6.1. Basically: Processing + does a "to primitive" on the operands, which means it then ends up with numbers, strings, booleans, nulls, or undefineds. If either operand, after coercion to primitive, is a string, it then coerces the other to string and does concatenation; otherwise, it coerces both to number and does addition.
The result of "to primitive" on objects depends on the object, but most objects end up coercing to string or number.
The only objects that can't be coerced to primitives are objects lacking both a toString and a valueOf*; in that case, §8.12.8 tells us that a TypeError is thrown. In practice, the only objects that won't have toString and valueOf are ones that A) Don't have their own copy of them, and B) Have no prototype or a prototype chain that doesn't include Object.prototype. For example, an object created by Object.create(null) (which has no prototype).
So no, there's no combination of values for which + is undefined but there is an edge case where it will throw a TypeError:
(Normal case) Both operands can coerced to a primitive, and any primitive can be coerced to a string or a number (the resulting number may, of course, be NaN), and so we get a resulting value, or
(Edge case) One of the two operands can't be converted to primitive, in which case we get a TypeError
* Okay, they don't have to be missing: If the object has toString but toString returns a non-primitive, then we get the TypeError if the object doesn't have valueOf or the object has valueOf, but valueOf also returns a non-primitive. In practice, toString and valueOf are supposed to return primitives.

Semantic meaning in operations between Arrays

I was coding today and I made a mistake while declaring an Array of Arrays in javascript (using the literal notation), I had forgotten to place a comma between each element in the array. After some further tests I got:
[[0][0]] gave me [0]
[[1][2]] gave me [undefined]
[0][0] gave me 0
[3][3] gave me undefined
[3]3 gave me a SyntaxError: Unexpected number
[][] gave me SyntaxError: Unexpected token ]
[3]*3 gave me 9 (Number not inside an array)
[3,4]*3 gave me NaN
[3,3]*[3,3] gave me NaN
[3,3]*[[3,3]] gave me NaN
[3,3][3,3] gave me undefined
[3,3][[3,3]] gave me undefined
At first I thought that this behavior might be mathematical vector/matrix multiplication, but that does not seem to be the case.
So the no operator between each array is clearly different than adding a * operator, and the * operator itself does not seem to perform neither scalar multiplication nor matrix multiplication.
The minus and division signs seems to always yield NaN and the plus sign seems to call a toString on both arrays and concat the strings.
I found that to be very odd, what is the semantic meaning behind operations between two arrays? To me the thing that makes most sense is to either always give errors when declaring Array Array and to always give NaN when declaring Array _operator_ Array. But that is not the case at all. The + sign at least makes sense because Array inherits from Object and that also happens if you try new Date() + new Date() (and this automatic toString call might be useful sometimes, although I would not design the language this way).
the * operator itself does not seem to perform neither scalar multiplication nor matrix multiplication. The minus and division signs seems to always yield NaN.
Indeed. *, / and - only work on numbers, and they will cast their operands to numbers. The array [3,3] will in that process first be converted to the string "3,3, which is not a valid number, therefore NaN as the result. With [3]*3 it "works" because the array is casted to the number 3. Similarly, [3]-[1] would yield 2.
So the no operator between each array is clearly different than adding a * operator. I found that to be very odd, what is the semantic meaning behind operations between two arrays?
If you did place "no operator" between the arrays, the latter ones are no arrays any more. The first pair of […] does build an array literal, but all the following […] are property accessors in bracket notation. [0][0] just accesses the first item of the [0] array which happens to be 0.
That is why […][] is a syntax error - the bracket notation needs an expression for the property name.
What you did see with […][…,…] was the comma operator to delimit expressions, it is not an array literal but parsed as …[(…, …)]. Your [3,3][3,3] is equivalent to [3,3][3], and accessing the fourth item in the array [3,3] will yield undefined.
The + sign at least makes sense because Array inherits from Object and that also happens if you try new Date() + new Date() (and this automatic toString call might be useful sometimes, although I would not design the language this way).
Yes, the + operator is complicated in JS, as it deals with multiple different types and does either (numeric) addition or (string) concatenation.
This is even worse when you use it on objects. In that case, it is tried to be cast to a primitive value (string, number, boolean), and for that the [[DefaultValue]] algorithm is applied (with no hint). When both operands are identified to be numeric, the addition is performed - this can even happen to objects:
> 1 + {valueOf: function(){ return 2; }}
3
> 1 + {valueOf: function(){ return "2"; }}
"12"

Javascript 0 == '0'. Explain this example

I found these examples in another thread but I don't get it.
0 == '0' // true
0 to the left I converted to false(the only number that does that). The right is a non empty string which converts to true.
So how can
false == true --> true
What have I missed?
Here is an official answer to your question (the quoted parts, link at the bottom) and analysis:
Equality (==)
The equality operator converts the operands if they are not of the same type, then applies strict comparison. If either operand is a number or a boolean, the operands are converted to numbers if possible; else if either operand is a string, the string operand is converted to a number if possible. If both operands are objects, then JavaScript compares internal references which are equal when operands refer to the same object in memory.
Syntax
x == y
Examples
3 == 3 // true
"3" == 3 // true
3 == '3' // true
This means, as I read it, that the first 3 (integer) is converted to string to satisfy the comparison, so it becomes '3' == '3', which is true, same as in your case with zeroes.
NOTE: I assume that the converion may vary in different JS engines, although they have to be unified under ECMAScript specifiction - http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3 (quoted #Derek朕會功夫). This assumption is made on a subjective and imperative opinion that not all browsers and JavaScript engines out there are ECMAScript compliant.
Identity / strict equality (===)
The identity operator returns true if the operands are strictly equal (see above) with no type conversion.
The Identity / strict equality (===) found on the same resource at the end of the answer will skip the automatic type conversion (as written above) and will perform type checking as well, to ensure that we have exact match, i.e. the expression above will fail on something like:
typeof(int) == typeof(string)
This is common operator in most languages with weak typing:
http://en.wikipedia.org/wiki/Strong_and_weak_typing
I would say that one should be certain what a function/method will return, if such function/method is about to return numbers (integers/floating point numbers) it should stick to that to the very end, otherwise opposite practices may cut your head off by many reasons and one lays in this question as well.
The above is valid for other languages with weak typing, too, like PHP for example.
Read more of this, refer second head (Equality operators):
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators
When you use == JavaScript will try very hard to convert the two things you are trying to compare to the same type. In this case 0 is converted to '0' to do the comparison, which then results in true.
You can use ===, which will not do any type coercion and is best practice, to get the desired result.
Equality operator
The equality operator converts the operands if they are not of the same type, then applies strict comparison. If either operand is a number or a boolean, the operands are converted to numbers if possible; else if either operand is a string, the string operand is converted to a number if possible. If both operands are objects, then JavaScript compares internal references which are equal when operands refer to the same object in memory.
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators
JavaScirpt Table Equality: http://dorey.github.io/JavaScript-Equality-Table/

JavaScript Adding Booleans

console.log(true+true); //2
console.log(typeof(true+true)); //number
console.log(isNaN(true+true)); //false
Why is adding together 2 boolean types yielding a number? I kind of understand that if they didn't equal (1/0 (binary?)) it would be awkward to try to perform arithmetic on a boolean type, but I can't find the reasoning behind this logic.
It works like that because that's how it's specified to work.
EcmaScript standard specifies that unless either of the arguments is a string, the + operator is assumed to mean numeric addition and not string concatenation. Conversion to numeric values is explicitly mentioned:
Return the result of applying the addition operation to ToNumber( lprim) and ToNumber(rprim).
(where lprim and rprim are the primitive forms of the left-hand and the right-hand argument, respectively)
EcmaScript also specifies the To Number conversion for booleans clearly:
The result is 1 if the argument is true. The result is +0 if the argument is false.
Hence, true + true effectively means 1 + 1, or 2.
Javascript is loosely typed, and it automatically converts things into other things to fit the situation. That's why you can do var x without defining it as an int or bool
http://msdn.microsoft.com/en-us/library/6974wx4d(v=vs.94).aspx
Javascript is a dynamically typed language, because you don't have to specify what type something is when you start, like bool x or int i. When it sees an operation that can't really be done, it will convert the operands to whatever they need to be so that they can have that operation done on them. This is known as type coercion. You can't add booleans, so Javascript will cast the booleans to something that it can add, something like a string or a number. In this case, it makes sense to cast it to a number since 1 is often used to represent true and 0 for false. So Javascript will cast the true's to 1s, and add them together

Categories

Resources