It gets a bit clearer, when you look at the intermediary results before the modulo operation first:
> Math.pow(17, 22)
1.1745628765211486e+27
>>> pow(17, 22)
1174562876521148458974062689
As you can see, the Python result has a lot more digits than the JavaScript result. This is due to how each language handles numbers or integers.
Python has an int type, which is basically unlimited: “These represent numbers in an unlimited range, subject to available (virtual) memory only.” So as long as you have memory available, the integers that can be represented with this type can be as big as you want—without any loss in precision.
Enter JavaScript and the ECMA standard. Unlike Python and other languages, we only have a single type responsible for all numeric types: Number. This type holds integers and decimals without any differentiation. They are internally represented as double precision floating point numbers. As such, they are subject to those restrictions, allowing only a certain amount of precision for big numbers. Hence, the best you can get for 17^22 is the above result, with the rest of the precision lost in the process.
If you are not too focused on performance, you could write your own pow function that additional takes a third parameter to apply a modulo operation to the result, similar to how Python’s pow does.
function modpow (base, exponent, modulo) {
var result = base;
while (exponent > 1 ) {
result = (result * base) % modulo;
exponent--;
}
return result;
}
Of course, this is a lot less efficient than the internal pow and only works for integer exponents, but at least it would solve your job correctly:
> modpow(17, 22, 21)
4