Here is the bitwise addition. In this way, you can add arbitrarily large numbers, not only limited to the maximum allowable value, for js it is 1.79E + 308 , so this is called long arithmetic. Since ordinary numbers are limited, the result is given as a string.
First, consider the add function.
function add(a, b) { return res(a.toString().split(""), b.toString().split(""), "").toString(); }
Supposedly it accepts numbers, but it can accept a string with numbers.
That is what happens in it. First, if a not a string, it turns into a string. Then using the split function is divided into an array of individual numbers. The same happens with the second term - b . Then the res function is called, which will calculate our answer. Since the number can be arbitrarily long, we will collect the answer one digit at a time, as a string. The initial value of the response string "" , on each recursive call to res we will calculate one digit.
Next, the res function is called with the initial parameters, it starts recursion.
Now according to the res : function, this function just adds. Almost ordinary, the usual school addition to the column.

If you rename the parameters a bit:
function res(a, b, result, carry) {
We have terms a and b , an intermediate result in result , a transfer from the previous digit to carry .
If the left and right terms are empty and there is no transfer, then the result is ready, and we return it.
if(a.length == 0 && b.length == 0 && !carry) return result;
We bring out complex expressions in a local variable for simplicity. We are going to get the lower digit of each item, and remove this figure from further consideration. For this we use the pop function, which removes the last element of the array, gives it to us.
var left = parseInt(a.pop() || '0', 10); var right = parseInt(b.pop() || '0',10);
That is, for the array ['9', '8', '7'] , the pop() function returns '7' , and only ['9', '8'] remains from the array.
If the array was actually empty, the function will return undefined . This is where the || '0' trick comes in handy. || '0' : if the result was a digit, it will not spoil at the same time, but undefined will turn into '0' !
We converted the resulting digit-string into a number using parseInt .
Next we add the left and right numbers, and do not forget to add the transfer:
var l = left + right + (carry || 0);
If the transfer is not specified, using the same trick we turn it into 0 .
It remains to call the function recursively, so that it performs the same operation with higher digits. Only need to recalculate the new transfer:
return res(a, b, l + (result || ""), l > 9? 1:0); }
But, as @ Ni55aN correctly noted, this function does not always work correctly.
Need to change it a bit.
- Add a base, for example 10. The fact is that we add one digit at a time. But we can, in theory, and put in large chunks. It would be an optimization.
- Then, we have a bug: we add a part to the result, without “biting off” the transferred high-order bit from it. This is wrong, you need to remove too much.
- For the case of two terms, the transfer is not greater than 1, but if we want to generalize the code to the case of a larger number of terms, the exact value will need to be calculated.
We write the exact calculation "for growth".
function res(a, b, result, carry, base) { if(a.length == 0 && b.length == 0 && !carry) return result; //берем младшие разряды var left = parseInt(a.pop() || '0', 10); var right = parseInt(b.pop() || '0', 10); //складываем и добавляем перебор с прошлой итерации var l = left + right + (carry || 0); //вызываем для следующих разрядов, правильно вычисляя добавленную цифру и цифру переноса return res(a, b, l % base + (result || ""), Math.floor(l/base), base); } function add(a, b) { return res(a.toString().split(""), b.toString().split(""), "","",10).toString(); }
Another trifle: we accumulate the result in the parameter so that we can call the function recursively at the end of the function itself. This is the so-called tail recursion , many compilers can handle it efficiently without occupying the stack.
Example:
$(function() { $('#left,#right').change(function(){ var left = $('#left').val().trim(); var right = $('#right').val().trim(); console.log(left,right,isNaN(left),isNaN(right)); if(isNaN(left) || isNaN(right)) return; $('#sum').html(add(left,right)); }); function res(a, b, result, carry, base) { if (a.length == 0 && b.length == 0 && !carry) return result; //берем младшие разряды var left = parseInt(a.pop() || '0', 10); var right = parseInt(b.pop() || '0', 10); //складываем и добавляем перебор с прошлой итерации var l = left + right + (carry || 0); //вызываем для следующих разрядов return res(a, b, l % base + (result || ""), Math.floor(l / base), base); } function add(a, b) { return res(a.toString().split(""), b.toString().split(""), "", "", 10).toString(); } });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> left: <input type="text" id="left" /> <br/>right: <input type="text" id="right" /> <br/>sum: <span id="sum"></span>