JS 實(shí)現(xiàn)計(jì)算器詳解及實(shí)例代碼(一)
Javascript 實(shí)現(xiàn)計(jì)算器:
系列文章:
JS 實(shí)現(xiàn)計(jì)算器詳解及實(shí)例代碼(一)
Javascript 實(shí)現(xiàn)計(jì)算器時(shí)間功能詳解及實(shí)例(二)
小型JavaScript計(jì)算器
自己尋思出的解決方案,比較笨拙的方法,雖然完成了但是還有不少bug,用的方法也不是最有效的,基本功能算是完成了,一些小的細(xì)節(jié)地方也考慮到了,但是還有其他的細(xì)節(jié)需要處理。
總體設(shè)計(jì)思路是,先畫草圖 -> 設(shè)計(jì)UI -> 編寫UI代碼 -> 編寫CSS -> 編寫JS邏輯代碼;
面板(main-board)
面板整體尺寸設(shè)計(jì)
標(biāo)題欄(board-title)
- 字體: font: 30px/50px “Comic Sans MS”, “微軟雅黑”;
- 寬高:(100%, 50px);
屏顯區(qū)(board-result)
- 數(shù)字顯示區(qū)(result-up):
- 表達(dá)式顯示區(qū)(result-down):
按鈕區(qū)(board-keys),使用表格完成,然后給每個(gè)td添加onclick事件
完成界面
導(dǎo)入新字體
// main.css @font-face { font-family: Lovelo-Black;/×定義font的名字×/ src: url('font/Lovelo Black.otf');/*把下載的字體文件引入進(jìn)來×/ }
代碼分析
代碼組織結(jié)構(gòu)
計(jì)算器對(duì)象:Calculator;
計(jì)算器屬性:
- bdResult: 計(jì)算器面板上的屏顯區(qū)DOM對(duì)象;
- operator:操作符數(shù)組,包括'+,-,×,÷,=';
- digits:有效數(shù)字字符,包括'0-9'和點(diǎn)'.';
- dot, equal, zero:'.', ‘=', ‘0'對(duì)應(yīng)三個(gè)字符,點(diǎn),等號(hào),字符'0';
- digit:屏顯區(qū)上層的顯示的當(dāng)前輸入的數(shù)字;
- expression:屏顯區(qū)下層的顯示的輸入的數(shù)字和操作符組成的表達(dá)式;
- resSpan:屏顯區(qū)上層的顯示當(dāng)前數(shù)字的span對(duì)象;
- resDown:屏顯區(qū)下層的顯示表達(dá)式的div對(duì)象;
- last:上一次輸入的按鈕內(nèi)容;
- allDigits:用表達(dá)式解析出來的表達(dá)式中所有的有效數(shù)字;
- ops:用表達(dá)式字符串解析出來的表達(dá)式中所有的操作符;
- hasEqual:判斷是否按了'='等號(hào)的標(biāo)識(shí)符;
- lastRes:上一次計(jì)算出來的結(jié)果[TODO],尚未用到,待實(shí)現(xiàn)可以連續(xù)計(jì)算;
計(jì)算器方法:
- init:計(jì)算器初始化方法;
- addTdClick:給每個(gè)td即計(jì)算器按鈕添加點(diǎn)擊事件;
- calculatorClickEvent:點(diǎn)擊事件;
- btnClickHanlder:點(diǎn)擊事件處理函數(shù);
- showCurrRes:處理屏顯區(qū)上層和下層將要顯示的內(nèi)容;
- showText:將通過showCurrRes處理的結(jié)果顯示出來;
- addZero:對(duì)表達(dá)式前面加'0'操作;
- calResult:計(jì)算結(jié)果;
- clearData:清空數(shù)據(jù);
- hasOperator:判斷表達(dá)式中是否有操作符;
- isOperator:判斷當(dāng)前字符是否是操作符;
- delHeadZero:刪除表達(dá)式開頭的'0';
輔助方法
- getResSpan:獲取屏顯上層的span對(duì)象;
- $tag:根據(jù)標(biāo)簽名去獲取標(biāo)簽對(duì)象;
- $:根據(jù)id去獲取DOM對(duì)象;
代碼邏輯
使用方法
- 引入Calculator.js文件(在編寫完UI的基礎(chǔ)上)
- 創(chuàng)建對(duì)象并初始化:new Calculator().init();
計(jì)算器對(duì)象
// 計(jì)算器對(duì)象 function Calculator() { // 私有屬性 this.bdResult = $("board-result"); // 計(jì)算機(jī)面板結(jié)果顯示區(qū)對(duì)象 this.operator = ['+', '-', '×', '÷', '=']; this.digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.']; // 組成有效數(shù)字的數(shù)字?jǐn)?shù)組 this.dot = '.'; this.equal = '='; this.zero = '0'; this.digit = ""; // 當(dāng)前輸入的數(shù)字 this.expression = ""; // 表達(dá)式 this.resSpan = getResSpan(); // 數(shù)字顯示區(qū) this.resDown = $("result-down"); // 表達(dá)式顯示區(qū) this.last = ""; // 上一次按下的按鈕內(nèi)容 this.allDigits = []; // 從表達(dá)式中獲取的所有數(shù)字組成的數(shù)組,將用來和ops中的操作符對(duì)應(yīng)計(jì)算出結(jié)果 this.ops = []; // 所有操作符組成的數(shù)組 this.hasEqual = false; // 判斷是否按下了'='鍵 this.lastRes = 0; // 上一次計(jì)算的結(jié)果,即上一次按等號(hào)后計(jì)算出的值 // 私有方法 }
添加點(diǎn)擊事件(注意this在閉包里的引用問題)
// 為td添加點(diǎn)擊事件 Calculator.prototype.addTdClick = function () { var tds = $tag("td"); var that = this; // 需要注意保存this的引用 // 為每個(gè)td添加點(diǎn)擊事件 for (var i = 0; i < tds.length; i++) { tds[i].onclick = function (){ // alert(this.innerText); var text = this.innerText; that.calculatorClickEvent(text); }; } };
計(jì)算器點(diǎn)擊事件處理入口
// 計(jì)算器按鈕事件 Calculator.prototype.calculatorClickEvent = function (btnText) { // 上一個(gè)按鍵是'=' if (this.hasEqual) { this.hasEqual = false; this.clearData(); } // 結(jié)果顯示在board-result里 if (btnText != "AC" && btnText != "CE") { this.btnClickHanlder(btnText); } else { // AC或CE清零 this.clearData(); } };
計(jì)算器點(diǎn)擊事件處理程序
// 計(jì)算器的按鍵事件處理 Calculator.prototype.btnClickHanlder = function (btnText) { if ((btnText >= '0' && btnText <= '9') || btnText == this.dot) { // 數(shù)字鍵處理 // 如果上一個(gè)是操作符,則清空當(dāng)前數(shù)字區(qū) if (this.isOperator(this.last)) { this.resSpan.innerText = ''; this.digit = ''; } else if ((btnText == this.dot) && (this.last == this.dot)) { // 如果上一個(gè)也是點(diǎn),則對(duì)本次的點(diǎn)按鈕不做響應(yīng) return; } this.digit += btnText; this.expression += btnText; } else if (this.isOperator(btnText)) { // 操作符處理 // 如果當(dāng)前表達(dá)式為'0',按'=',不給響應(yīng) if ((btnText == this.equal) && (this.resDown.innerText == this.zero || this.resDown.innerText == "")) return; // 如果上一個(gè)是非'='的操作符則不進(jìn)行處理 if (!this.isOperator(this.last) && btnText == this.equal) { // '='處理 this.showCurrRes(this.zero, this.expression + btnText); // 計(jì)算結(jié)果顯示在表達(dá)式區(qū)域 return; } else if (this.isOperator(this.last)) { // 上一個(gè)是操作符,此次的操作符不做記錄 return; } else { this.expression += btnText; } } this.showCurrRes(this.digit, this.expression); this.last = btnText; };
處理將要顯示的表達(dá)式和當(dāng)前輸入的數(shù)字
// 顯示當(dāng)前結(jié)果的觸發(fā)方法 Calculator.prototype.showCurrRes = function (digit, expression) { if (!expression) return; this.showText(digit, expression); // 1. 沒有'=',表示還沒有到計(jì)算結(jié)果的時(shí)候,直接退出 if (expression.indexOf(this.equal) == -1) return; // 計(jì)算出了結(jié)果 this.hasEqual = true; // 2. 處理只按了數(shù)字然后直接按了等號(hào)的情況,即:'234='則直接返回234 var tmpStr = this.delHeadZero(expression.substr(0, expression.length - 1)); // 去掉最后一個(gè)'=' if (!this.hasOperator(tmpStr)) { this.showText(tmpStr, expression + tmpStr); return; } // 3. 處理表達(dá)式字符串,且計(jì)算出結(jié)果 var start = 0; for (var i = 0; i < expression.length; i++) { var c = expression[i]; if (this.isOperator(c)) { // 操作符 this.ops.push(c); // 保存操作符 var numStr = expression.substr(start, i + 1); // 數(shù)字字符串 var number = 0; // 浮點(diǎn)數(shù)和整型處理 if (numStr.indexOf(this.dot)) { number = parseFloat(numStr); } else { number = parseInt(numStr); } this.allDigits.push(number); // 保存數(shù)字 start = i + 1; // 重設(shè)數(shù)字起始位置,即操作符的下一個(gè)字符開始 } } // 用allDigits和ops去計(jì)算結(jié)果 var res = this.calResult(); // 保存此次計(jì)算結(jié)果,作為下一次計(jì)算用 [TODO] this.lastRes = res; // 將結(jié)果顯示出來 this.showText(res + '', expression + res); };
將處理結(jié)果顯示到屏顯區(qū)
// 將表達(dá)式和計(jì)算結(jié)果顯示到屏顯區(qū) Calculator.prototype.showText = function (digitStr, expression) { // 先刪除開頭的'0' var expStr = this.delHeadZero(expression); var digStr = this.delHeadZero(digitStr); // 然后再根據(jù)情況決定是否添加'0' var tmp = expression == this.zero ? expression : this.addZero(expStr);; var dig = digitStr == this.zero ? digitStr : this.addZero(digStr); this.resSpan.innerText = dig; // 如果表達(dá)式第一個(gè)是操作符,則表示之前按的是'0',則給補(bǔ)上'0',因?yàn)榍懊鎸㈤_頭的'0'都刪掉了 if (this.isOperator(tmp[0])) { tmp = this.zero + tmp; } this.resDown.innerText = tmp; }
計(jì)算結(jié)果函數(shù)
// 計(jì)算結(jié)果 Calculator.prototype.calResult = function () { var first = 0; var second = 0; var res = 0; for (var i = 0; i < this.ops.length; i++) { first = this.allDigits[i]; second = this.allDigits[i + 1]; switch (this.ops[i]) { case '+': res = first + second; break; case '-': res = first - second; break; case '×': res = first * second; break; case '÷': res = first / second; break; default: break; } this.allDigits[i + 1] = res; } return res; };
清空數(shù)據(jù)
// 計(jì)算完一次,清空所有數(shù)據(jù),以備下次計(jì)算使用 Calculator.prototype.clearData = function () { this.allDigits = []; this.ops = []; this.expression = this.zero; this.digit = ''; this.resSpan.innerText = this.zero; this.resDown.innerText = this.zero; };
輔助函數(shù)
處理表達(dá)式開頭的'0'問題(第一個(gè)按鈕是0鍵或者第一個(gè)是小于1的浮點(diǎn)數(shù),表達(dá)式需要補(bǔ)零;)
// 開頭添加'0',防止重復(fù)出現(xiàn)或者沒有'0'情況 Calculator.prototype.addZero = function (expression) { if (!expression) return this.zero; if (expression[0] == this.dot) { // 浮點(diǎn)數(shù) return this.zero + expression; } else { return expression; } };
開頭去零函數(shù)
// 去開頭的零 Calculator.prototype.delHeadZero = function (str) { // 先把開頭的‘0'都刪掉 var tmp = ""; tmp = str.replace(/^[0]+/gi, ""); if (tmp[0] == this.dot) { // 浮點(diǎn)數(shù)重新補(bǔ)上'0' tmp = this.zero + tmp; } return tmp; };
判斷字符串里是否含有操作符
// 判斷表達(dá)式中是否含有操作符 Calculator.prototype.hasOperator = function (str) { if (!str) return; for (var i = 0; i < this.operator.length; i++) { if (str.indexOf(this.operator[i]) >= 0) { return true; } } return false; };
其他函數(shù)
// 獲取輸入的數(shù)字顯示區(qū)對(duì)象 function getResSpan() { return $("result-up").getElementsByTagName("span")[0]; } // 根據(jù)標(biāo)簽名獲取DOM對(duì)象 function $tag(tagName) { return document.getElementsByTagName(tagName); } // 根據(jù)ID獲取DOM對(duì)象 function $(id) { return document.getElementById(id); }
問題
- 文字底部顯示:通過設(shè)置行高處理;
- 通過一次性解析表達(dá)式需要考慮表達(dá)式開頭是否需要'0'存在;
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- AngularJS ng-blur 指令詳解及簡單實(shí)例
- 老生常談onBlur事件與onfocus事件(js)
- jsp+ajax實(shí)現(xiàn)的局部刷新較驗(yàn)驗(yàn)證碼(onblur事件觸發(fā)較驗(yàn))
- javascript委托(Delegate)blur和focus用法實(shí)例分析
- Js中的onblur和onfocus事件應(yīng)用介紹
- AngularJS Controller作用域
- JSP頁面跳轉(zhuǎn)方法小結(jié)
- 詳解js中==與===的區(qū)別
- js時(shí)間控件只顯示年月
- java后端把數(shù)據(jù)轉(zhuǎn)換為樹,map遞歸生成json樹,返回給前端(后臺(tái)轉(zhuǎn)換)
- 完美解決node.js中使用https請(qǐng)求報(bào)CERT_UNTRUSTED的問題
- JS實(shí)現(xiàn)iframe自適應(yīng)高度的方法示例
- Javascript blur與click沖突解決辦法
- jsp頁面顯示數(shù)據(jù)庫的數(shù)據(jù)信息表
相關(guān)文章
JavaScript基礎(chǔ)語法與數(shù)據(jù)類型介紹
這篇文章介紹了JavaScript基礎(chǔ)語法與數(shù)據(jù)類型,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02使用Math.floor與Math.random取隨機(jī)整數(shù)的方法詳解
本篇文章對(duì)使用Math.floor與Math.random取隨機(jī)整數(shù)的方法進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05整理JavaScript對(duì)DOM中各種類型的元素的常用操作
這篇文章主要介紹了JavaScript對(duì)DOM中各種類型的元素的常用操作整理,DOM操作是JavaScript程序的一大根本任務(wù),需要的朋友可以參考下2016-05-05使用RequireJS優(yōu)化JavaScript引用代碼的方法
這篇文章主要介紹了使用RequireJS優(yōu)化JavaScript引用代碼的方法,RequireJS是一款人氣JS庫,需要的朋友可以參考下2015-07-07JavaScript 高級(jí)篇之DOM文檔,簡單封裝及調(diào)用、動(dòng)態(tài)添加、刪除樣式(六)
學(xué)習(xí)是有趣的,但有過濾的學(xué)習(xí)內(nèi)容就更好,本博主就專門為剛接觸javascript客戶端編程的朋友提供及分享個(gè)人學(xué)習(xí)經(jīng)歷!建議大家看看:(湯姆大叔的博客)2012-04-04JavaScript中函數(shù)的常用寫法及調(diào)用方法
這篇文章介紹了JavaScript中函數(shù)的常用寫法及調(diào)用方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06淺談JavaScript中的apply/call/bind和this的使用
apply/call/bind三者的聯(lián)系就在于,都可以用來改變函數(shù)中 this 指向的值,且第一個(gè)參數(shù)為要指向的 this 的值,apply的第二個(gè)參數(shù)(或 bind 與 call 的不定參數(shù))為要傳入的參數(shù)。這就不得不提及 javascript 中函數(shù)的 this 的指向了。下面我們來簡單探討下2017-02-02