Referencing the Ecmascript 5 spec: 11.10 Binary Bitwise Operators, namely
The production A : A @ B, where @ is one of the bitwise operators in
  the productions above (&; ^; |), is evaluated as follows:  
Let lref be the result of evaluating A.
  Let lval be GetValue(lref).
  Let rref be the result of evaluating B.
  Let rval be GetValue(rref).
  Let lnum be ToInt32(lval).
  Let rnum be ToInt32(rval).
  Return the result of applying the bitwise operator@ to lnum and rnum. The result is a signed 32 bit integer.  
And noting that ToInt32() is defined as
Let number be the result of calling ToNumber on the input argument.
  If number is NaN, +0, −0, +∞, or −∞, return +0.
  Let posInt be sign(number) * floor(abs(number)).
  Let int32bit be posInt modulo 2^32; that is, a finite integer value k of Number type with positive sign and less than 2^32 in magnitude such that the mathematical difference of posInt and k is mathematically an integer multiple of 2^32.
  If int32bit is greater than or equal to 2^31, return int32bit − 2^32, otherwise return int32bit.
It then logically follows (which you can confirm in your own console) that for example
((Math.pow(2, 32)) + 2) | 0 === 2
(Math.pow(2, 31)) | 0 === -2147483648 === -(Math.pow(2, 31))
And so forth.  
Shortly put, the operation turns the number to a 32-bit integer (which has its knacks, see the second example above and the ToInt32() definition for an explanation) and then does a logical or with zero which doesn't change the output beyond the first conversion.
Essentially it's a very cost-efficient way to turn a number into a 32-bit integer because 1) it relies on browser's built-in ToInt32(); and 2) ToInt32(0) short-circuits to 0 (see the spec above) and therefore adds practically no additional overhead.