Does Javascript handle integer overflow and underflow? If yes, how?

The maximum and minimum is +/- 9007199254740992

Try these Number type properties:

alert([Number.MAX_VALUE, Number.MIN_VALUE]);

From the ECMAScript 2020 language specification, section "The Number Type":

Note that all the positive and negative mathematical integers whose magnitude is no greater than 253 are representable in the Number type (indeed, the mathematical integer 0 has two representations, +0 and −0).

Test:

var x = 9007199254740992;
var y = -x;
x == x + 1; // true !
y == y - 1; // also true !

In a simple test, when I try this:

var max = Number.MAX_VALUE;
var x = max + 10;

var min = Number.MIN_VALUE;
var y = min / 10;

I find that x and max have the same value (in Chrome, IE and Firefox) so it appears that some overflows are just pegged to the max value. And, y gets pegged to 0 so some underflows seem to go to zero.

Ahhh, but it is not quite that simple. Not all overflows go to Number.MAX_VALUE and not all underflows go to Number.MIN_VALUE. If you do this:

var max = Number.MAX_VALUE;
var z = max * 2;

Then, z will be Infinity.

It turns out that it depends upon how far you overflow/underflow. If you go too far, you will get INFINITY instead. This is because of the use of IEEE 754 round-to-nearest mode where the max value can be considered nearer than infinity. See Adding to Number.MAX_VALUE for more detail. Per that answer, values of 1.7976931348623158 × 10308 or greater round to infinity. Values between Number.MAX_VALUE and that will round to Number.MAX_VALUE.

To, make things even more complicated, there is also something as gradual underflow which Javascript supports. This is where the mantissa of the floating point value has leading zeroes in it. Gradual underflow allows floating point to represent some smaller numbers that it could not represent without that, but they are represented at a reduced precision.

You can see exactly where the limits are:

>>> Number.MAX_VALUE + 9.979201e291
1.7976931348623157e+308
>>> Number.MAX_VALUE + 9.979202e291
Infinity

Here's a runnable snippet you can try in any browser:

var max = Number.MAX_VALUE;
var x = max + 10;

var min = Number.MIN_VALUE;
var y = min / 10;

var z = max * 2;

document.getElementById("max").innerHTML = max;
document.getElementById("max10").innerHTML = x;
document.getElementById("min").innerHTML = min;
document.getElementById("min10").innerHTML = y;
document.getElementById("times2").innerHTML = z;
body {
    font-family: "Courier New"; 
    white-space:nowrap;
}
Number.MAX_VALUE &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <span id="max"></span><br>
Number.MAX_VALUE + 10 = <span id="max10"></span><br>
<br>
Number.MIN_VALUE &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <span id="min"></span><br>
Number.MIN_VALUE / 10 = <span id="min10"></span><br>  
<br>
Number.MAX_VALUE * 2 &nbsp;= <span id="times2"></span><br>

Number

In JavaScript, number type is 64 bit IEEE 754 floating point number which is not an integer. So it don't follow common patterns of integer overflow / underflow behavior in other languages.

As the floating point number use 53 bits for base part. It may represent numbers in range Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER (-253+1 to 253-1) without floating point errors. For numbers out of this range, it may be rounded to nearest number available, or may be Infinity if it is too large.

Bit-wise operator

Bit-wise operator treat operand 32 bit integers. And common integer overflow may happened as in other languages. Only last 32 bits may be kept after calculate. For example, 3<<31 would results -2147483648.

>>> treat operand as unsigned 32 bit integers. All other operator treat operand as signed 32 bit integers. If you want to convert signed integer to unsigned, you may write value >>> 0 to do the trick. To convert back, use value | 0.

If you want to shift an integer with 33, it will actually be shifted with 1.

BigInt

Just like Java's java.math.BigInteger, BigInt supports unbounded integers (still bound by your memory limit though). So integer overflow may never happened here.

TypedArray

For TypedArray types, when an integer out of supported range assigned, it got truncated as what other languages do when converting integers, by keeping least significant bits. For example new Int8Array([1000])[0] got -24.

asm.js

The same rules for bit-wise operator applied here. The value would be trucked back as what | 0 or >>> 0 do.