This is due to the fact that the comparison and equality operators in JavaScript work (main attention):
For relational abstract comparisons (for example, <=), the operands are first converted to primitives , then to the same type before the comparison.
[...]
The equality operator converts the operands if they are not of the same type, then applies a strict comparison. If either the operand is number or boolean, the operands are converted to numbers, if possible; else, if any operand is a string, the operand of a string, if possible, is converted to a number. If both operands are objects, then JavaScript compares internal links that are equal when using operands, refer to the same object in memory.
Therefore, when two instantaneous objects are compared with inequalities, they are first converted to numbers. For Moment.js objects, these are milliseconds since unix midnight UTC, January 1, 1970.
In your favorite browser console window / node REPL:
> +moment() <- 1412332710977
To verify equality == runtime performs a reference comparison between two objects, which returns false for two different instances of the moment, even if they both refer to the same date / time.
The lack of a default overload of .equals() or operator==() in JavaScript makes this behavior rather inconsistent, especially if you come from other languages.
Also note that the Moment isBefore / isAfter functions are isAfter slower, since they first clone both instant objects before doing the comparison (this is because the optional argument isBefore / isAfter to indicate which time component to compare (for example, 'hour' ), and it clones regardless of whether this argument is present or not).
The moment here suffers from the presence of mutable objects, so it makes a protective clone() in the first place, and then further from the fact that it has not optimized the general path, where it is not actually needed.