在学完页面布局课程后,专门学习了网格布局,用起来很方便,于是决定做一个小 demo,实现了一个简单的计算器,使用了 Grid 布局,使用了事件委托来分别处理不同按钮的点击,使用了 Vue.js 框架,使用了栈数据结构来处理十进制转二进制的运算,使用了递归来处理先乘除后加减的运算,然后通过条件判断来处理所有情况。
写代码的顺序是这样的:先写 HTML 结构,再写 CSS 样式,再写 JS 逻辑代码,写逻辑代码时先将核心算法写出来,即加减乘除和进制转换算法,并处理细节部分,再将其他的按钮逻辑写出来,并处理细节部分。
请问老师有什么好的建议给到我,关于写代码的思维方式,以及代码质量。谢谢老师
这是 parse.js 文件,专门处理加减乘除算法,使用了递归
function parse(content) {
var index = content.indexOf("+");
//只要还有加号就还在这个判断句中进行逐层递归
if(index > -1) {
return parse(content.substring(0,index)) + parse(content.substring(index+1));
}
//当在表达式没有发现加号时,会到这里,递归就是层层递进,再将结果逐层回溯
index = content.lastIndexOf("-");
if(index > -1) {
return parse(content.substring(0,index)) - parse(content.substring(index+1));
}
//要先放置加减再放置乘除,这样才会先乘除后加减
index = content.indexOf("*");
if(index > -1) {
return parse(content.substring(0,index)) * parse(content.substring(index+1));
}
index = content.lastIndexOf("/")
if(index > -1) {
return parse(content.substring(0,index)) / parse(content.substring(index+1));
}
//递归的基准条件
return Number(content);
}
这是 binary.js 文件,专门处理十进制转二进制算法,使用了栈数据结构
let Stack = (function() {
const items = new WeakMap();
class Stack {
constructor() {
items.set(this, []);
}
push(element) {
let s=items.get(this);
s.push(element);
}
pop() {
let s=items.get(this);
return s.pop();
}
peek() {
let s=items.get(this);
return s[s.length-1];
}
isEmpty() {
let s=items.get(this);
return s.length == 0;
}
clear() {
let s=items.get(this);
s=[];
}
size() {
let s=items.get(this);
return s.length;
}
}
return Stack;
})();
function divideBy2(decNumber) {
var remStack=new Stack();
var rem;
var binaryString='';
while (decNumber>0) {
rem=Math.floor(decNumber%2);
remStack.push(rem);
decNumber=Math.floor(decNumber/2);
}
while (!remStack.isEmpty()) {
binaryString += remStack.pop().toString();;
}
return binaryString;
}
这是主代码:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>A Simple Calculator</title>
<style media="screen">
html * {
margin: 0;
padding: 0;
}
.calculator {
width: 200px;
margin: 100px auto;
}
.calculator input {
box-sizing: border-box;
padding: 0 6px;
background: #ccc;
width: 200px;
height: 50px;
font-size: 32px;
color: #FFF;
text-align: right;
outline: none;
/* 让字体缩进,往左缩进200像素 */
text-indent: -200px;
}
/* 处理 placeholder 的颜色 */
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: #FFF;
}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: #FFF;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: #FFF;
}
:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: #FFF;
}
/* 使用 Grid 布局 */
.calculator .container {
display: grid;
width: 50%;
grid-template-columns: repeat(4, 50px);
grid-template-rows: repeat(4, 50px);
}
.item {
color: #FFF;
font-size: 32px;
text-align: center;
background: #CCC;
border: 1px solid #BBB;
outline: none;
}
.item-17 {
grid-column-start: span 2;
}
</style>
</head>
<body>
<section id="root" class="calculator">
<input type="text" v-model="inputValue" placeholder="0" disabled="disabled" ref="one">
<!-- 使用事件委托分别处理子元素的点击事件 -->
<div class="container" @click="handleBtnClick">
<button class="item item-1">AC</button>
<button class="item item-2">C</button>
<button class="item item-3">二</button>
<button class="item item-4">/</button>
<button class="item item-5">7</button>
<button class="item item-6">8</button>
<button class="item item-7">9</button>
<button class="item item-8">*</button>
<button class="item item-9">4</button>
<button class="item item-10">5</button>
<button class="item item-11">6</button>
<button class="item item-12">-</button>
<button class="item item-13">1</button>
<button class="item item-14">2</button>
<button class="item item-15">3</button>
<button class="item item-16">+</button>
<button class="item item-17">0</button>
<button class="item item-18">.</button>
<button class="item item-19">=</button>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./parse.js"></script>
<script src="./binary.js"></script>
<script>
var vm = new Vue({
el: '#root',
data: {
inputValue: '',
content: '',
lastBtn: [],
record: false,
cookies: [],
case: false,
n: 0
},
methods: {
handleBtnClick(e) {
var id = e.target.innerHTML
//当点击数字按钮或者加减乘除运算符按钮时,将 id 逐一拼接在 content 中
//当连续点击加减乘除运算符按钮时,将第二次的 id 覆盖掉第一次的 id
//为了验证上一次的 id 和这一次的 id 是否都是加减乘除运算符,我们将每一次点击的 id 都通过 record 布尔值记录在 lastBtn 中,只有当 id 为加减乘除运算符时,将 record 设置成 true
if(id === "+" || id === "-" || id === "*" || id === "/") {
this.record = true
if(!this.content) {
this.content = "0"
this.inputValue = this.content
}
}
this.lastBtn.push(this.record)
if(this.record) this.record = false
if(!isNaN(id) || id === "+" || id === "-" || id === "*" || id === "/") {
if(this.lastBtn[this.lastBtn.length-2] && this.lastBtn[this.lastBtn.length-1]) {
this.content = this.content.substring(0,this.content.length-1)
}
this.content += id
this.inputValue = this.content
}
//按等于号时,将结果显示出来,并将结果缓存起来
if(id === "=") {
this.inputValue = parse(this.content) + ""
this.cookies.push(this.inputValue)
this.case = true
//我们只需要记录上一次的 id
this.lastBtn = this.lastBtn.splice(this.lastBtn.length-1,1)
}
//当计算过一次结果后
if(this.case) {
//如果直接点击加减乘除运算符,将 content 清空,并且将结果赋值给 content
if(id === "+" || id === "-" || id === "*" || id === "/") {
this.content = this.cookies[this.cookies.length-1] + id
this.inputValue = this.content
this.case = false
}
//如果直接点击数字按钮,就是新的一轮运算
if(!isNaN(id)) {
this.content = id
this.inputValue = this.content
this.case = false
}
//如果直接点击小数点,就是新的一轮运算
if(id === ".") {
this.content = "0."
this.inputValue = this.content
this.case = false
}
}
//当点击 "." 时,总体上分为 content 不为空和为空两种情况分别怎么处理
//当 content 不为空时,有三种情况分别怎么处理
if(id === ".") {
if(this.content) {
if(!isNaN(this.content) && this.content.indexOf(".") === -1) {
this.content += id
this.inputValue = this.content
}else if(isNaN(this.content) && this.lastBtn[this.lastBtn.length-2]) {
this.content += "0."
this.inputValue = this.content
}else if(isNaN(this.content) && !this.lastBtn[this.lastBtn.length-2]){
//取 content 最后一个数字或者小数
var re1 = /\d+(\.\d+)?$/
var lastNum1 = this.content.match(re1)
if(lastNum1[0].indexOf(".") === -1) {
this.content += id
this.inputValue = this.content
}
}
}else {
this.content = "0."
this.inputValue = this.content
}
}
//在输入数字按钮时,不能出现 0 打头的正整数
if(!isNaN(id)) {
if(!isNaN(this.content)) {
var re = /^[0]\d/
if(re.test(this.content)) {
this.content = this.content.substring(1)
this.inputValue = this.content
}
}else {
//取 content 最后一个数字或者小数
var re = /\d+(\.\d+)?$/
var lastNum = this.content.match(re)
var index = lastNum.index
//只有整数才能进入到下面条件句中,会忽略小数
re = /^[0]\d/
if(re.test(lastNum[0])) {
this.content = this.content.substring(0,index) + this.content.substring(index+1)
this.inputValue = this.content
}
}
}
//当点击 "二" 时,可以将十进制转换成二进制
if(id === "二") {
if(!isNaN(this.inputValue))
this.inputValue = divideBy2(this.inputValue)
}
//当点击 "AC" 时,初始化所有数据
if(id === "AC") {
this.inputValue = ""
this.content = ""
this.lastBtn = []
this.cookies = []
this.case = false
this.$refs.one.style.fontSize = "32px"
}
//当点击 "C" 时,可以逐一删除信息
if(id === "C") {
this.content = this.content.substring(0,this.content.length-1)
this.inputValue = this.content
}
}
},
//使字体大小随着字符数量的增加而缩小,当缩小到一定范围后,就不再缩小,而是往左缩进 200 像素
watch: {
inputValue() {
this.n = this.inputValue.length
if(this.n > 10 && this.n < 27){
this.$refs.one.style.fontSize = 308/this.n + "px"
}else if(this.n <= 10){
this.$refs.one.style.fontSize = "32px"
}
}
}
})
</script>
</body>
</html>
这是效果图,基本功能已得到实现!