因为在项目中使用到了WebSocket的自定义二进制协议,须要将二进制转为后端服务中定义的Long型。而在JavaScript中的Number类型因为自身缘由,并不能彻底表示Long型的数字,所以须要咱们经过其余的方式来对Long型值进行存储。javascript
在GitHub中,有一个实现了在JavaScript中存储Long型的对象,具体代码能够戳此。下面,咱们经过简单讲解一下这个库的具体实现来看看如何在JavaScript中实现一个Long型。若是你了解了这个实现原理,那么与之相似的,在JavaScript中实现一个Long Long型或者其余类型的方法也是相似的。java
其实,Long的实现很简单,咱们如今只要回归到计算机的本质便可。在计算机中,其实存储的都是01字符串。例如,Int占4个字节(咱们以32位操做系统为例),而Long则占8个字节。git
咱们在存储中只须要将数据经过二进制进行存储,而后在操做中对二进制进行操做便可。github
下面咱们简单的来介绍一下Long的各个表明操做和思想。后端
在Long型对象中,咱们采用了高32位和低32位,以及加上一个符号位判断的值,用来进行数据的存储,具体格式以下:函数
function Long(low, high, unsigned) {
this.low = low | 0;
this.high = high | 0;
this.unsigned = !!unsigned;
}
复制代码
经过对高低位的存储,从而让两个Number来同时表示一个Long型的高位和低位,从而知足了数值的长度要求。oop
咱们目前只介绍一个经过字符串来说数据从String型转换为Long型,其余的转换例如从Number转换为Long型是相似的,咱们就不过多赘述了。学习
先看实现函数:ui
function fromString(str, unsigned, radix) {
// 处理异常状况
if (str.length === 0)
throw Error('empty string');
//处理为0的状况
if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity")
return ZERO;
//处理只有两个参数的状况
if (typeof unsigned === 'number') {
// For goog.math.long compatibility
radix = unsigned,
unsigned = false;
} else {
unsigned = !! unsigned;
}
radix = radix || 10;
if (radix < 2 || 36 < radix)
throw RangeError('radix');
var p;
if ((p = str.indexOf('-')) > 0)
throw Error('interior hyphen');
else if (p === 0) {
// 转为正值处理
return fromString(str.substring(1), unsigned, radix).neg();
}
// 从最高位分8位处理一次,若是长度超过8位,则先处理高位,而后将高位直接乘以进制的8次方,再处理低后8位,循环到最后8位为止
var result = ZERO;
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i),
value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = fromNumber(pow_dbl(radix, size));
result = result.mul(power).add(fromNumber(value));
} else {
result = result.mul(radixToPower);
result = result.add(fromNumber(value));
}
}
result.unsigned = unsigned;
return result;
}
复制代码
下面咱们简单的说下这个函数的实现:this
Long型转换为字符串的方式,与字符串转换为Long型的步骤差很少,差很少是一个相反的过程。
LongPrototype.toString = function toString(radix) {
radix = radix || 10;
if (radix < 2 || 36 < radix)
throw RangeError('radix');
if (this.isZero())
return '0';
//若是是负值,Unsigned型的Long值永远不会为负值
if (this.isNegative()) {
if (this.eq(MIN_VALUE)) {
// We need to change the Long value before it can be negated, so we remove
// the bottom-most digit in this base and then recurse to do the rest.
var radixLong = fromNumber(radix),
div = this.div(radixLong),
rem1 = div.mul(radixLong).sub(this);
return div.toString(radix) + rem1.toInt().toString(radix);
} else
return '-' + this.neg().toString(radix);
}
//每次处理6位,处理方式与字符串转换过来是相似的,和数学中十进制转换为N进制方法相同——相除法
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned),
rem = this;
var result = '';
while (true) {
var remDiv = rem.div(radixToPower),
intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0,
digits = intval.toString(radix);
rem = remDiv;
if (rem.isZero())
return digits + result;
else {
while (digits.length < 6)
digits = '0' + digits;
result = '' + digits + result;
}
}
};
复制代码
上面这个函数的实现步骤正好相反:
0x80000000
时,则对它进行了单独处理。18 = 2 * 8 + 2; 2 = 0 * 8 + 2;
,所以结果为0x22。只是,在此函数中,一次相除的是进制数的6次方,其他步骤是相似的。在知道了Long型的存储本质是使用高低各32位之后,Long型的运算其实就已经了解了。咱们只须要针对特定的操做进行相对应的二进制操做,那么咱们就可以获得相对应的结果,下面的实例是Long型相加的操做,咱们简单了解下:
LongPrototype.add = function add(addend) {
if (!isLong(addend))
addend = fromValue(addend);
// 将每一个数字分红4个16比特的块,而后将这些块加起来
var a48 = this.high >>> 16;
var a32 = this.high & 0xFFFF;
var a16 = this.low >>> 16;
var a00 = this.low & 0xFFFF;
var b48 = addend.high >>> 16;
var b32 = addend.high & 0xFFFF;
var b16 = addend.low >>> 16;
var b00 = addend.low & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
};
复制代码
经过上面的操做咱们就能够知道,Long型的四则运算等操做其实都是经过二进制和位运算来实现的。并无咱们想象中的那么神秘。
其实,经过阅读Long.js
库的源码你就会发现,在JavaScript中实现一个Long型并不难,也许仍是一个听简单的事情,不太重要的是咱们可能想象不到这种的实现方式。所以,这个也证实了咱们在思考一个问题问题的同时,咱们也应该多从事情的本质来考虑,这样就有可能获得解决方案。
Long.js
的代码中添加了一些中文的注释,若是有须要能够到我folk的仓库进行阅读学习。