成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

如何在JavaScript中實(shí)現(xiàn)一個(gè)Long型——Long.js源碼學(xué)習(xí)與分析

fancyLuo / 3808人閱讀

摘要:目標(biāo)在中,有一個(gè)實(shí)現(xiàn)了在中存儲(chǔ)型的對象,具體代碼可以戳此。下面,我們通過簡單講解一下這個(gè)庫的具體實(shí)現(xiàn)來看看如何在中實(shí)現(xiàn)一個(gè)型。如果你了解了這個(gè)實(shí)現(xiàn)原理,那么與之類似的,在中實(shí)現(xiàn)一個(gè)型或者其他類型的方法也是類似的。

背景

由于在項(xiàng)目中使用到了WebSocket的自定義二進(jìn)制協(xié)議,需要將二進(jìn)制轉(zhuǎn)為后端服務(wù)中定義的Long型。而在JavaScript中的Number類型由于自身原因,并不能完全表示Long型的數(shù)字,因此需要我們通過其他的方式來對Long型值進(jìn)行存儲(chǔ)。

目標(biāo)

在GitHub中,有一個(gè)實(shí)現(xiàn)了在JavaScript中存儲(chǔ)Long型的對象,具體代碼可以戳此。下面,我們通過簡單講解一下這個(gè)庫的具體實(shí)現(xiàn)來看看如何在JavaScript中實(shí)現(xiàn)一個(gè)Long型。如果你了解了這個(gè)實(shí)現(xiàn)原理,那么與之類似的,在JavaScript中實(shí)現(xiàn)一個(gè)Long Long型或者其他類型的方法也是類似的。

具體實(shí)現(xiàn)

其實(shí),Long的實(shí)現(xiàn)很簡單,我們現(xiàn)在只要回歸到計(jì)算機(jī)的本質(zhì)即可。在計(jì)算機(jī)中,其實(shí)存儲(chǔ)的都是01字符串。例如,Int占4個(gè)字節(jié)(我們以32位操作系統(tǒng)為例),而Long則占8個(gè)字節(jié)。

我們在存儲(chǔ)中只需要將數(shù)據(jù)通過二進(jìn)制進(jìn)行存儲(chǔ),然后在操作中對二進(jìn)制進(jìn)行操作即可。

下面我們簡單的來介紹一下Long的各個(gè)代表操作和思想。

大致步驟 數(shù)據(jù)存儲(chǔ)

在Long型對象中,我們采用了高32位和低32位,以及加上一個(gè)符號位判斷的值,用來進(jìn)行數(shù)據(jù)的存儲(chǔ),具體格式如下:

function Long(low, high, unsigned) {
    this.low = low | 0;
    this.high = high | 0;
    this.unsigned = !!unsigned;
}

通過對高低位的存儲(chǔ),從而讓兩個(gè)Number來同時(shí)表示一個(gè)Long型的高位和低位,從而滿足了數(shù)值的長度要求。

轉(zhuǎn)換為Long型

我們目前只介紹一個(gè)通過字符串來講數(shù)據(jù)從String型轉(zhuǎn)換為Long型,其他的轉(zhuǎn)換例如從Number轉(zhuǎn)換為Long型是類似的,我們就不過多贅述了。

先看實(shí)現(xiàn)函數(shù):

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;
        
    //處理只有兩個(gè)參數(shù)的情況
    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) {
        // 轉(zhuǎn)為正值處理
        return fromString(str.substring(1), unsigned, radix).neg();
    }
    
    // 從最高位分8位處理一次,如果長度超過8位,則先處理高位,然后將高位直接乘以進(jìn)制的8次方,再處理低后8位,循環(huán)到最后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;
}

下面我們簡單的說下這個(gè)函數(shù)的實(shí)現(xiàn):

對數(shù)據(jù)進(jìn)行異常處理,排除一些邊界條件。

如果字符串為一個(gè)帶"-"號的值,則轉(zhuǎn)換為正值進(jìn)行處理。

如果字符串為一個(gè)常規(guī)的Long型值,則先從最前面的8位開始處理,將其通過指定的進(jìn)制轉(zhuǎn)換為Long型的值。

處理接下來的8位,并且將之前的結(jié)果乘以進(jìn)制數(shù)的8次方,即數(shù)字高地位的合并。例如:18 = 1 * 10^1 + 8。

循環(huán)上面的操作,直到剩余的字符串長度小于8為止,即可結(jié)束,得到轉(zhuǎn)換之后的Long型。

轉(zhuǎn)換為字符串

Long型轉(zhuǎn)換為字符串的方式,與字符串轉(zhuǎn)換為Long型的步驟差不多,差不多是一個(gè)相反的過程。

LongPrototype.toString = function toString(radix) {
    radix = radix || 10;
    if (radix < 2 || 36 < radix)
        throw RangeError("radix");
    if (this.isZero())
        return "0";
    //如果是負(fù)值,Unsigned型的Long值永遠(yuǎn)不會(huì)為負(fù)值
    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位,處理方式與字符串轉(zhuǎn)換過來是類似的,和數(shù)學(xué)中十進(jìn)制轉(zhuǎn)換為N進(jì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;
        }
    }
};

上面這個(gè)函數(shù)的實(shí)現(xiàn)步驟正好相反:

處理各種邊界條件

如果Long型為一個(gè)負(fù)值,則轉(zhuǎn)換為正值進(jìn)行處理,如果Long型為0x80000000時(shí),則對它進(jìn)行了多帶帶處理。

在處理正值Long型為字符串時(shí),操作方法與我們數(shù)學(xué)中教的轉(zhuǎn)換進(jìn)制的相除法類似,具體操作為:先除以需要轉(zhuǎn)換的進(jìn)制數(shù),得到結(jié)果和余數(shù),將結(jié)果重新作為被除數(shù)相除直到被除數(shù)為0,再將余數(shù)拼接起來即可。例如:18(10進(jìn)制)轉(zhuǎn)換為8進(jìn)制時(shí),操作是:18 = 2 * 8 + 2; 2 = 0 * 8 + 2;,因此結(jié)果為0x22。只是,在此函數(shù)中,一次相除的是進(jìn)制數(shù)的6次方,其余步驟是類似的。

通過上面的操作得到字符串后返回即可。

Long型相加

在知道了Long型的存儲(chǔ)本質(zhì)是使用高低各32位以后,Long型的運(yùn)算其實(shí)就已經(jīng)了解了。我們只需要針對特定的操作進(jìn)行相對應(yīng)的二進(jìn)制操作,那么我們就能夠得到相對應(yīng)的結(jié)果,下面的實(shí)例是Long型相加的操作,我們簡單了解下:

LongPrototype.add = function add(addend) {
    if (!isLong(addend))
        addend = fromValue(addend);
    // 將每個(gè)數(shù)字分成4個(gè)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型的四則運(yùn)算等操作其實(shí)都是通過二進(jìn)制和位運(yùn)算來實(shí)現(xiàn)的。并沒有我們想象中的那么神秘。

總結(jié)

其實(shí),通過閱讀Long.js庫的源碼你就會(huì)發(fā)現(xiàn),在JavaScript中實(shí)現(xiàn)一個(gè)Long型并不難,也許還是一個(gè)聽簡單的事情,不過重要的是我們可能想象不到這種的實(shí)現(xiàn)方式。因此,這個(gè)也證明了我們在思考一個(gè)問題問題的同時(shí),我們也應(yīng)該多從事情的本質(zhì)來考慮,這樣就有可能得到解決方案。

附錄

我在Long.js的代碼中添加了一些中文的注釋,如果有需要可以到我folk的倉庫進(jìn)行閱讀學(xué)習(xí)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/83258.html

相關(guān)文章

  • WebSocket系列之JavaScript中數(shù)字?jǐn)?shù)據(jù)如何轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)

    摘要:以和為例,說明中的數(shù)字?jǐn)?shù)據(jù)如何轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)。對象用來表示通用的固定長度的原始二進(jìn)制數(shù)據(jù)緩沖區(qū)。中的數(shù)字?jǐn)?shù)據(jù)如何轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)對和有了一個(gè)大概的了解,下面讓我們來看下它是如何進(jìn)行二進(jìn)制數(shù)據(jù)操作的。 概述 本文主要通過對JavaScript中數(shù)字?jǐn)?shù)據(jù)與二進(jìn)制數(shù)據(jù)之間的轉(zhuǎn)換,讓讀者能夠了解在JavaScript中如何對數(shù)字類型(包括但不限于Number類型)進(jìn)行處理。 二進(jìn)制數(shù)據(jù)在日常...

    MASAILA 評論0 收藏0
  • 【C語言進(jìn)階學(xué)習(xí)】一、數(shù)據(jù)的存儲(chǔ) (深度剖析數(shù)據(jù)內(nèi)存中的存儲(chǔ))

    摘要:的理解和區(qū)別代表有符號,整數(shù)在內(nèi)存中存儲(chǔ)的二進(jìn)制位的最高位為符號位,表示負(fù)數(shù),表示正數(shù)。那接下來我們來學(xué)習(xí)數(shù)據(jù)在所開辟的內(nèi)存空間時(shí)如何存儲(chǔ)的。請看下面例子為什么內(nèi)存中存儲(chǔ)的是補(bǔ)碼對于整數(shù)來說數(shù)據(jù)存放內(nèi)存中其實(shí)存放的是補(bǔ)碼。 ...

    AprilJ 評論0 收藏0
  • Hack Python 整數(shù)對象

    摘要:在類型系統(tǒng)部分中定義如下類型表示對象,如對象表示例化后的類型類型初始化函數(shù)表示在初始化時(shí)調(diào)用的用來初始化類型的函數(shù),如構(gòu)造函數(shù)表示構(gòu)造對象需要的函數(shù),如。 背景 寫這篇文章的原因是目前在看《Python源碼剖析》[1],但是這本書的作者陳儒老師剖析源碼的目的好像不是太明確,所以看上去是為了剖析源碼而剖析源碼,導(dǎo)致的結(jié)果是這本書里面的分析思路不太清楚(可能是我的理解問題),而且驗(yàn)證想法...

    sixgo 評論0 收藏0
  • 后臺開發(fā)常問面試題集錦(問題搬運(yùn)工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    spacewander 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<