因爲在項目中使用到了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的倉庫進行閱讀學習。