Simple JS calculator implementation code and js calculator code
Look at the calculators in your cell phone. They are divided into common calculators and scientific calculators.
If you think your head is not big enough, you can implement a normal version (supporting basic continuous operations such as positive, negative, addition, subtraction, multiplication, division, and not providing the brackets function)
See the figure below:
I. Knowledge preparation
1 + 1 =?
Normally, we can see that this expression knows how to calculate and how to calculate the result.
However, unlike computers, computers cannot recognize this string of expressions. They can only recognize specific rules: prefix expression + 1 or suffix expression 1 +
Example
(3 + 4) × 5-6 is the infix expression.
-× + 3 4 5 6 prefix expression
3 4 + 5 × 6-suffix expression
Therefore, to realize the automatic operation of the program, we need to convert the input data into a prefix or suffix expression.
The concepts of prefix, infix, suffix expression, and the method of mutual conversion are not mentioned here. This blog post is clear.
Therefore, the extension expression is used in the implementation of this calculator. Refer to the above articles and focus on these two algorithms:
Similar to a prefix expression, follow these steps:
(1) initialize two stacks: Operator stack S1 and stack S2 that stores intermediate results;
(2) scan the infix expression from left to right;
(3) press the operand into S2;
(4) When an operator is encountered, compare its priority with the S1 stack top operator:
(4-1) If S1 is null, or the top-end operator of the stack is left brackets (), the operator is directly added to the stack;
(4-2) Otherwise, if the priority is higher than the top operator of the stack, the operator is also pushed to S1 (note that the conversion to prefix expressions has a higher or the same priority, this does not include the same situation );
(4-3) otherwise, the operator at the top of the S1 stack will pop up and press it into S2, and then go to (4-1) again to compare with the operator at the top of the new stack in S1;
(5) parentheses:
(5-1) if it is a left brace "(", Press it directly into S1;
(5-2) if it is the right brace ")", the operator at the top of the S1 stack is popped up and pushed to S2 until the left brace is encountered. At this time, this pair of parentheses is discarded;
(6) Repeat steps (2) to (5) until the rightmost side of the expression;
(7) pop up the remaining operators in S1 and press them into S2;
(8) The elements in S2 are displayed and output in sequence. The reverse order of the result is the suffix expression corresponding to the infix expression (no reverse order is required when the prefix expression is converted ).
Similar to prefix expressions, the order is from left to right:
From left to right scan expression. When a number is encountered, the number is pushed into the stack. When an operator is encountered, the two numbers at the top of the stack are displayed, use operators to calculate them (the top element of the op stack) and import the result to the stack. Repeat the above process until the rightmost end of the expression, the final calculated value is the result of the expression.
For example, the suffix expression "3 4 + 5 × 6 -":
(1) scan from left to right and press 3 and 4 into the stack;
(2) When the + operator is encountered, 4 and 3 (4 is the top element of the stack, 3 is the top element of the stack, and note that it is compared with the prefix expression) are displayed, and the value of 3 + 4 is calculated, 7, and then 7 into the stack;
(3) add 5 to the stack;
(4) The next step is the X operator. Therefore, 5 and 7 are displayed, and 7 × 5 = 35 is calculated. The 35 is added to the stack;
(5) Add 6 to the stack;
(6) Finally, the-operator calculates the value of 35-6, that is, 29, and the final result is obtained.
II. Implementation Process
The first step is to build the Page Structure of the calculator. It is not a scientific calculator. It only provides basic computing functions, but can also perform operations instantly to display the complete infix expression, save the previous operation record.
First, I want to implement the decimal point function. However, the existence of the decimal point puts pressure on data storage and data display. This function is simply canceled.
1. Page Structure:
<h5> Calculation calculation </ h5>
<!-Calculator->
<div class = "calc-wrap">
<div class = "calc-in-out">
<!-Previous operation record->
<p class = "calc-history" title = ""> </ p>
<!-Input data->
<p class = "calc-in"> </ p>
<!-Output operation result->
<p class = "calc-out active"> </ p>
</ div>
<table class = "calc-operation">
<thead> </ thead>
<tbody>
<tr>
<td data-ac = "cls" class = "cls"> C </ td>
<td data-ac = "del"> ← </ td>
<td data-ac = "sq"> x <sup> 2 </ sup> </ td>
<td data-ac = "mul"> × </ td>
</ tr>
<tr>
<td data-val = "7"> 7 </ td>
<td data-val = "8"> 8 </ td>
<td data-val = "9"> 9 </ td>
<td data-ac = "div"> ÷ </ td>
</ tr>
<tr>
<td data-val = "4"> 4 </ td>
<td data-val = "5"> 5 </ td>
<td data-val = "6"> 6 </ td>
<td data-ac = "plus"> + </ td>
</ tr>
<tr>
<td data-val = "1"> 1 </ td>
<td data-val = "2"> 2 </ td>
<td data-val = "3"> 3 </ td>
<td data-ac = "minus">-</ td>
</ tr>
<td data-ac = "per">% </ td>
<td data-val = "0"> 0 </ td>
<td data-ac = "dot">. </ td>
<td data-ac = "eq" class = "eq"> = </ td>
</ tbody>
</ table>
</ div>
2. Combine a little style:
body {
padding: 20px;
font-family: Arial;
}
.calc-wrap {
width: 300px;
border: 1px solid #ddd;
border-radius: 3px;
}
.calc-operation {
width: 100%;
border-collapse: collapse;
}
.calc-in-out {
width: 100%;
padding: 10px 20px;
text-align: right;
box-sizing: border-box;
background-color: rgba (250, 250, 250, .9);
}
.calc-in-out p {
overflow: hidden;
margin: 5px;
width: 100%;
}
.calc-history {
margin-left: -20px;
font-size: 18px;
color: #bbb;
border-bottom: 1px dotted #ddf;
min-height: 23px;
}
.calc-in,
.calc-out {
font-size: 20px;
color: # 888;
line-height: 39px;
min-height: 39px;
}
.calc-in {
color: # 888;
}
.calc-out {
color: #ccc;
}
.calc-in.active,
.calc-out.active {
font-size: 34px;
color: # 666;
}
.calc-operation td {
padding: 10px;
width: 25%;
text-align: center;
border: 1px solid #ddd;
font-size: 26px;
color: # 888;
cursor: pointer;
}
.calc-operation td: active {
background-color: #ddd;
}
.calc-operation .cls {
color: # ee8956;
}
This static calculator is rough ~~
3. JS logic
This part is the focus, step by step
The first is to monitor the calculator, that is, this form, you can use the event delegation method to monitor and process on the parent node
// Bind event
bindEvent: function () {
var that = this;
that. $ operation.on ('click', function (e) {
e = e || window.event;
var elem = e.target || e.srcElement,
val,
action;
if (elem.tagName === 'TD') {
val = elem.getAttribute ('data-val') || elem.getAttribute ('data-ac');
Listening to the data, you only get a certain value / operator on the page, so you need to store the data to form an infix, then convert the infix to a suffix, and finally calculate by the suffix
// infix expression
this.infix = [];
// Suffix expression
this.suffix = [];
// Suffix expression operation result set
this.result = [];
Implemented according to the algorithm steps, no parentheses are used here, if necessary, you can modify the judgment conditions at the corresponding position ~
// Infix expression to suffix
infix2Suffix: function () {
var temp = [];
this.suffix = [];
for (var i = 0; i <this.infix.length; i ++) {
// numeric value, directly press in
if (! this.isOp (this.infix [i])) {
this.suffix.push (this.infix [i]);
}
else {
if (! temp.length) {
temp.push (this.infix [i]);
}
else {
var opTop = temp [temp.length-1];
// Loop to determine operator priority and push higher operator into suffix expression
if (! this.priorHigher (opTop, this.infix [i])) {
while (temp.length &&! this.priorHigher (opTop, this.infix [i])) {
this.suffix.push (temp.pop ());
opTop = temp [temp.length-1];
}
}
// Push the current operator into the suffix expression
temp.push (this.infix [i]);
}
}
}
// Push the remaining arithmetic symbols into
while (temp.length) {
this.suffix.push (temp.pop ());
}
},
// Suffix expression calculation
calcSuffix: function () {
this.result = [];
for (var i = 0; i <this.suffix.length; i ++) {
// numeric value, directly pushed into the result set
if (! this.isOp (this.suffix [i])) {
this.result.push (this.suffix [i]);
}
// Operator, take two items from the result set for operation, and put the result into the result set
else {
this.result.push (this.opCalc (this.result.pop (), this.suffix [i], this.result.pop ()));
}
}
// At this time there is only one value in the result set, which is the result
return this.result [0];
}
In fact, when implementing it, you will find that the infix and suffix are just a difficulty. The more complicated thing is the state change (or data change) of the entire calculator
In this simple calculator, there are data and operations such as numbers (0-9), operators (+-* /), operations (clear and delete), pre-operations (percent sign squared), decimal point, and immediate operations
If it is a scientific calculator, it is more complicated, so it is critical to sort out how to control these things, and the most important one is the construction and storage of infix expressions.
When the + sign is clicked continuously, it does not conform to the actual operation, so a variable lastVal is needed to record the last value, which is updated with the operation, and then judged to prevent the program from making mistakes
After clicking the = sign, we can continue to use this result for calculation, or restart the calculation
// Build infix expression
buildInfix: function (val, type) {
// After the direct click equal operation,
if (this.calcDone) {
this.calcDone = false;
// Click on the number again to start a new calculation
if (! this.isOp (val)) {
this.resetData ();
}
// Click on the operator again to continue the operation using the current result value
else {
var re = this.result [0];
this.resetData ();
this.infix.push (re);
}
}
var newVal;
...
Click delete, delete Except for one digit, instead of deleting a number directly, then update the value of the infix expression
// delete operation
if (type === 'del') {
newVal = this.infix.pop ();
// delete the last digit
newVal = Math.floor (newVal / 10);
if (newVal) {
this.infix.push (newVal);
}
this.lastVal = this.infix [this.infix.length-1];
return this.infix;
}
The addition operation needs to be considered more, such as continuous continuous operators, continuous numbers, operator +-connected with numbers to indicate positive and negative numbers, connection access to the decimal point, etc.
// Add operation, first determine whether the operator is repeated
else if (type === 'add') {
// Two consecutive operators
if (this.isOp (val) && this.isOp (this.lastVal)) {
return this.infix;
}
// two consecutive numbers
else if (! this.isOp (val) &&! this.isOp (this.lastVal)) {
newVal = this.lastVal * 10 + val;
this.infix.pop ();
this.infix.push (this.lastVal = newVal);
return this.infix;
}
// The first number is positive or negative
if (! this.isOp (val) && this.infix.length === 1 && (this.lastVal === '+' || this.lastVal === '-')) {
newVal = this.lastVal === '+'? val: 0-val;
this.infix.pop ();
this.infix.push (this.lastVal = newVal);
return this.infix;
}
this.infix.push (this.lastVal = val);
return this.infix;
}
In many operations, the calculator needs to perform calculations in real time. To simplify the code, it can be packaged into a method and called at the corresponding position.
// immediate calculation
calculate: function (type) {
this.infix2Suffix ();
var suffixRe = this.calcSuffix ();
if (suffixRe) {
this. $ out.text ('=' + suffixRe)
.attr ('title', suffixRe)
.removeClass ('active');
// If the equality operation is performed directly
if (type === 'eq') {
this. $ in.removeClass ('active');
this. $ out.addClass ('active');
// Set the mark: the calculation is currently displayed
this.calcDone = true;
this.lastVal = suffixRe;
// Set history
var history = this.infix.join ('') + '=' + suffixRe;
this. $ history.text (history) .attr ('title', history);
}
}
},
The rest is the processing after clicking, which is a variety of call processing. Passing data-> building infix processing data-> infix to suffix-> suffix calculation
For example, click the number
// Number: 0-9
if (! isNaN (parseInt (val, 10))) {
// Build infix expression and display
var infixRe = that.buildInfix (parseInt (val, 10), 'add');
that. $ in.text (infixRe.join ('')). addClass ('active');
that.calculate ();
return;
}
Another example is a few pre-calculations, which are actually similar.
// Pre-calculation: percentage, decimal point, square
else if (['per', 'dot', 'sq']. indexOf (action)! == -1) {
if (! that.infix.length || that.isOp (that.lastVal)) {
return;
}
if (action === 'per') {
that.lastVal / = 100;
} else if (action === 'sq') {
that.lastVal * = that.lastVal;
} else if (action === 'dot') {
// that.curDot = true;
}
// Rebuild the infix expression
var infixRe = that.buildInfix (that.lastVal, 'change');
that. $ in.text (infixRe.join ('')). addClass ('active');
that.calculate ();
}
The above is the implementation steps of this simple calculator. There are too many changes and I ca n’t guarantee that I wo n’t make mistakes.
The basic logic is the same. If you want to add the functions of scientific calculators such as decimal point operation, bracket operation, sine and cosine, you should implement it yourself. . Big brain. .
$ (function () {
function Calculator ($ dom) {
this. $ dom = $ ($ dom);
// Historical operations
this. $ history = this. $ dom.find ('. calc-history');
// input area
this. $ in = this. $ dom.find ('. calc-in');
// output area
this. $ out = this. $ dom.find ('. calc-out');
this. $ operation = this. $ dom.find ('. calc-operation');
// Operator mapping
this.op = {
'plus': '+',
'minus': '-',
'mul': '*',
'div': '/'
};
this.opArr = ['+', '-', '*', '/'];
// infix expression
this.infix = [];
// Suffix expression
this.suffix = [];
// Suffix expression operation result set
this.result = [];
// store the most recent value
this.lastVal = 0;
// The current calculation is equal to completion
this.calcDone = false;
// The decimal point (.) Related value is currently being corrected
this.curDot = false;
this.init ();
}
Calculator.prototype = {
constructor: Calculator,
// initialize
init: function () {
this.bindEvent ();
},
// Bind event
bindEvent: function () {
var that = this;
that. $ operation.on ('click', function (e) {
e = e || window.event;
var elem = e.target || e.srcElement,
val,
action;
if (elem.tagName === 'TD') {
val = elem.getAttribute ('data-val') || elem.getAttribute ('data-ac');
// Number: 0-9
if (! isNaN (parseInt (val, 10))) {
// Build infix expression and display
var infixRe = that.buildInfix (parseInt (val, 10), 'add');
that. $ in.text (infixRe.join ('')). addClass ('active');
that.calculate ();
return;
}
action = val;
// Operation: clear, delete, calculate equal to
if (['cls', 'del', 'eq']. indexOf (action)! == -1) {
if (! that.infix.length) {
return;
}
// Clear data
if (action === 'cls' || (action === 'del' && that.calcDone)) {
that. $ in.text ('');
that. $ out.text ('');
that.resetData ();
}
// clear
else if (action === 'del') {
// Rebuild the infix expression
var infixRe = that.buildInfix (that.op [action], 'del');
that. $ in.text (infixRe.join ('')). addClass ('active');
that.calculate ();
}
// equals
else if (action === 'eq') {
that.calculate ('eq');
}
}
// Pre-calculation: percentage, decimal point, square
else if (['per', 'dot', 'sq']. indexOf (action)! == -1) {
if (! that.infix.length || that.isOp (that.lastVal)) {
return;
}
if (action === 'per') {
that.lastVal / = 100;
} else if (action === 'sq') {
that.lastVal * = that.lastVal;
} else if (action === 'dot') {
// that.curDot = true;
}
// Rebuild the infix expression
var infixRe = that.buildInfix (that.lastVal, 'change');
that. $ in.text (infixRe.join ('')). addClass ('active');
that.calculate ();
}
// Operator: +-* /
else if (that.isOp (that.op [action])) {
if (! that.infix.length && (that.op [action] === '*' || that.op [action] === '/')) {
return;
}
var infixRe = that.buildInfix (that.op [action], 'add');
that. $ in.text (infixRe.join ('')). addClass ('active');
}
}
});
},
resetData: function () {
this.infix = [];
this.suffix = [];
this.result = [];
this.lastVal = 0;
this.curDot = false;
},
// Build infix expression
buildInfix: function (val, type) {
// After the direct click equal operation,
if (this.calcDone) {
this.calcDone = false;
// Click on the number again to start a new calculation
if (! this.isOp (val)) {
this.resetData ();
}
// Click on the operator again to continue the operation using the current result value
else {
var re = this.result [0];
this.resetData ();
this.infix.push (re);
}
}
var newVal;
// delete operation
if (type === 'del') {
newVal = this.infix.pop ();
// delete the last digit
newVal = Math.floor (newVal / 10);
if (newVal) {
this.infix.push (newVal);
}
this.lastVal = this.infix [this.infix.length-1];
return this.infix;
}
// Add operation, first determine whether the operator is repeated
else if (type === 'add') {
// Two consecutive operators
if (this.isOp (val) && this.isOp (this.lastVal)) {
return this.infix;
}
// two consecutive numbers
else if (! this.isOp (val) &&! this.isOp (this.lastVal)) {
newVal = this.lastVal * 10 + val;
this.infix.pop ();
this.infix.push (this.lastVal = newVal);
return this.infix;
}
// The first number is positive or negative
if (! this.isOp (val) && this.infix.length === 1 && (this.lastVal === '+' || this.lastVal === '-')) {
newVal = this.lastVal === '+'? val: 0-val;
this.infix.pop ();
this.infix.push (this.lastVal = newVal);
return this.infix;
}
// TODO: decimal point operation
// if (this.isOp (val)) {
// this.curDot = false;
//}
// // Decimal point
// if (this.curDot) {
// var dotLen = 0;
// newVal = this.infix.pop ();
// dotLen = newVal.toString (). split ('.');
// dotLen = dotLen [1]? dotLen [1] .length: 0;
// newVal + = val / Math.pow (10, dotLen + 1);
// // Correct the exact value of decimal point operation
// newVal = parseFloat (newVal.toFixed (dotLen + 1));
// this.infix.push (this.lastVal = newVal);
// return this.infix;
//}
this.infix.push (this.lastVal = val);
return this.infix;
}
// Change operations, such as% pre-calculation
else if (type === 'change') {
this.infix.pop ();
this.infix.push (this.lastVal = val);
return this.infix;
}
},
// determine if it is an operator
isOp: function (op) {
return op && this.opArr.indexOf (op)! == -1;
},
// determine operator priority
priorHigher: function (a, b) {
return (a === '+' || a === '-') && (b === '*' || b === '/');
},
// Perform operator operations
opCalc: function (b, op, a) {
return op === '+'
? a + b
: op === '-'
? a-b
: op === '*'
? a * b
: op === '/'
? a / b
: 0;
},
// immediate calculation
calculate: function (type) {
this.infix2Suffix ();
var suffixRe = this.calcSuffix ();
if (suffixRe) {
this. $ out.text ('=' + suffixRe)
.attr ('title', suffixRe)
.removeClass ('active');
// If the equality operation is performed directly
if (type === 'eq') {
this. $ in.removeClass ('active');
this. $ out.addClass ('active');
// Set the mark: the calculation is currently displayed
this.calcDone = true;
this.lastVal = suffixRe;
// Set history
var history = this.infix.join ('') + '=' + suffixRe;
this. $ history.text (history) .attr ('title', history);
}
}
},
// Infix expression to suffix
infix2Suffix: function () {
var temp = [];
this.suffix = [];
for (var i = 0; i <this.infix.length; i ++) {
// numeric value, directly press in
if (! this.isOp (this.infix [i])) {
this.suffix.push (this.infix [i]);
}
else {
if (! temp.length) {
temp.push (this.infix [i]);
}
else {
var opTop = temp [temp.length-1];
// Loop to determine operator priority and push higher operator into suffix expression
if (! this.priorHigher (opTop, this.infix [i])) {
while (temp.length &&! this.priorHigher (opTop, this.infix [i])) {
this.suffix.push (temp.pop ());
opTop = temp [temp.length-1];
}
}
// Push the current operator into the suffix expression
temp.push (this.infix [i]);
}
}
}
// Push the remaining arithmetic symbols into
while (temp.length) {
this.suffix.push (temp.pop ());
}
},
// Suffix expression calculation
calcSuffix: function () {
this.result = [];
for (var i = 0; i <this.suffix.length; i ++) {
// numeric value, directly pushed into the result set
if (! this.isOp (this.suffix [i])) {
this.result.push (this.suffix [i]);
}
// Operator, take two items from the result set for operation, and put the result into the result set
else {
this.result.push (this.opCalc (this.result.pop (), this.suffix [i], this.result.pop ()));
}
}
// At this time there is only one value in the result set, which is the result
return this.result [0];
}
};
new Calculator ('. calc-wrap');
});
The above is the whole content of this article, I hope it will be helpful to everyone's learning, and I hope everyone will support the home of helpers.