記得之前看到過一個JS超大正整數相加的題目,今天想了下自己會怎麼實現,於是便進行了嘗試,代碼如下:
function bigAdd(a, b){
let lenA = a.length,
lenB = b.length,
curry = 0,
res = [];
let loopLen = Math.max(lenA, lenB );
for(let i = 0 ; i < loopLen ; i++){
const indexA = ( lenA - i - 1 ) > -1 ? lenA - i -1 : -1;
const indexB = ( lenB - i - 1) > -1 ? lenB - i -1 : -1;
const valA = indexA > -1 ? a[ indexA ] : 0 ;
const valB = indexB > -1 ? b[ indexB ] : 0 ;
res[i] = ( Number(valA) + Number(valB) + curry) % 10;
curry = Math.floor( ( Number(valA) + Number(valB) + curry ) / 10 );
}
return res.reverse().join('');
}
想法很簡單,就是按照加法的規律,從末尾開始一項一項的相加。
然後看了網上關於該題目的算法,真是驚歎別人算法的簡潔以及對於JS中類型轉換相關知識的巧妙應用。
function addBig(a, b){
let temp = 0,
res = '';
a = a.split('');
b = b.split('');
while( a.lenght || b.length || temp){
temp += ~~a.pop() + ~~b.pop();
res = (temp % 10 ) + res;
temp = temp > 9;
}
return res.replace(/^0+/g, '');
}
首先,這個代碼給人的感覺真的很精簡。但是其中其實隱藏了很多關於JS 中類型轉換的相關知識, 下面我們來一一分析。
1.爲什麼將a,b轉換爲數組
轉換爲數組的目的是爲了在while
循環體中使用 pop
方法來獲取數組中的最後一項。如果你不轉換爲數組,我們能想到的獲取字符串的最後一項的方法是sutstr(-1,1)
。但是這種方法只能獲取最後一項,無法動態的改變字符串本身。
2. 爲什麼while
的判斷條件是 a.lenght || b.length || temp
而不是a || b || temp
因爲循環體中每次的循環會使數組長度減少1,當數組爲空時,假設極端的條件下 a, b
都爲[]
,但是這個時候 [] || [] || 0
的結果是 true
, 循環永不結束。只因爲Boolean([]) = true
。
3. 爲什麼使用~~a.pop()
,如果只是 爲了將值轉換爲數字 ,爲何不使用Number
進行強制轉換不是更加易懂?
這個就十分精髓了,首先使用~
操作符進行取反是會將字符串轉換爲數字的,在進行一個取反操作,就轉換爲了字符串本身代表的數字,但不僅如此。考慮下我們數組循環的最後爲空時,[].pop()
的結果爲undefined
, 而 Number(undefined)
的結果爲NaN
,~NaN
的結果爲-1
, ~~NaN
的結果 爲0,當你使用NaN
進行加法運算時,一切結果都沒有了意義。所以這個地方必須爲~~a.pop()
, 而不能使用Number
進行強制轉換。
4. 爲什麼temp = temp > 9
,這是什麼鬼?
這一點其實也是用了JS中的隱式轉換,我們換個寫法你就明白了temp = temp > 9 ? 1 : 0
,temp
實際上表示的是進位的結果,在循環的開始我們使用temp += ~~a.pop() + ~~b.pop();
進行temp
的再賦值,實際等價於temp = (true | false) + Number
。 而Boolean
在進行數字運算時,true
會轉換爲1,false
會轉換爲0,此處巧妙的應用了該隱式轉換。
一個小小的算法,其中隱含了這麼多的基礎知識,所以還是需要夯實基礎啊!!!