马黑黑 发表于 2024-1-2 22:53

练手:JS计算器(第二版)

本帖最后由 马黑黑 于 2024-1-3 08:37 编辑 <br /><br /><style>
#ma { margin: 50px auto 0; padding: 8px; width: fit-content; max-width: 256px; box-sizing: border-box; height: fit-content; border-radius: 10px; background: linear-gradient(to right bottom, #666,tan); box-shadow: 2px 4px 8px black; }
#calcMsg, #calcRes { padding: 6px; color: #eee; font-size: 18px; word-break: break-all; }
#calcRes { text-align: right; color: #000; font-size: 20px; background: #eee; }
#calc { display: grid; place-items: center; grid-template-columns: repeat(4, 60px); grid-template-rows: repeat(5, 50px); }
.btn, .clear { width: 40px; height: 40px; border-radius: 50%; background: #aaa; box-shadow: inset 0 0 10px #000; font: normal 20px/40px sans-serif; text-align: center; user-select: none; cursor: pointer; }
.btn:hover, .clear:hover { background: #ccc; }
.num { color: darkred; font-weight: bold; }
</style>

<div id="ma">
        <div id="calcMsg">计算器</div>
        <div id="calcRes">0</div>
        <div id="calc">
                <div class="clear" id="clear">C</div>
                <div class="btn" data-idx="(">(</div>
                <div class="btn" data-idx=")">)</div>
                <div class="clear" id="del">←</div>
                <div class="btn num" data-idx="7">7</div>
                <div class="btn num" data-idx="8">8</div>
                <div class="btn num" data-idx="9">9</div>
                <div class="btn" data-idx="/">÷</div>
                <div class="btn num" data-idx="4">4</div>
                <div class="btn num" data-idx="5">5</div>
                <div class="btn num" data-idx="6">6</div>
                <div class="btn" data-idx="*">×</div>
                <div class="btn num" data-idx="1">1</div>
                <div class="btn num" data-idx="2">2</div>
                <div class="btn num" data-idx="3">3</div>
                <div class="btn num" data-idx="-">-</div>
                <div class="btn num" data-idx="0">0</div>
                <div class="btn" data-idx=".">.</div>
                <div class="btn" id="percent">%</div>
                <div class="btn" data-idx="+">+</div>
                <div class="clear" id="pow2">x²</div>
                <div class="clear" id="pow3">x³</div>
                <div class="clear" id="sqrt">√</div>
                <div class="clear" id="equal" data-idx="=">=</div>
        </div>
</div>

<script>

let btns = document.querySelectorAll('.btn'); /* 非id运算类按钮 */

/* 运算函数 */
let _eval = (fn) => new Function('return ' + fn)();

/* 处理运算结果 */
let calc = (str) => {
        let res;
        try {
                res = _eval(str);
                res = res == Infinity ? '∞' : num2Fixed(res);
        } catch(err) {
                res = '算式非法';
        }
        return res;
};

/* 非id类按钮点击事件 */
btns.forEach(item => {
        item.onclick = () => {
                if(calcRes.innerText === '0') {
                        if(item.dataset.idx === '0') {
                                calcRes.innerText = '0';
                        }else{
                                calcRes.innerText = isNaN(parseInt(item.dataset.idx)) && item.dataset.idx !== '(' ? '0' + item.dataset.idx : item.dataset.idx;
                        }
                }else{
                        calcRes.innerText += item.dataset.idx;
                }
                calcMsg.innerText = calcRes.innerText;
        }
});

/* C按钮点击事件 */
clear.onclick = () => {
        calcMsg.innerText = '计算器';
        calcRes.innerText = '0';
};

/* =按钮点击事件 */
equal.onclick = () => {
        let result = calc(calcRes.innerText);
        if(result === '算式非法') {
                calcMsg.innerText = result;
        }else{
                calcRes.innerText = result;
        }
        if(calcMsg.innerText.lastIndexOf('=') < 0) calcMsg.innerText += '=';
};

/* ←按钮点击事件 */
del.onclick = () => {
        calcRes.innerText = calcRes.innerText.slice(0,-1) || 0;
        calcMsg.innerText = calcRes.innerText;
};

/* 开方&幂运算按钮点击事件 */
.forEach(elm => {
        elm.onclick = () => {
                let str = calcRes.innerText;
                switch (elm.id) {
                        case 'pow2':
                                calcRes.innerText = sq_pow(str,'pow','2');
                                calcMsg.innerText = `${str}\u00b2`;
                                break;
                        case 'pow3':
                                calcRes.innerText = sq_pow(str,'pow','3');
                                calcMsg.innerText = `${str}\u00b3=`;
                                break;
                        default:
                                calcRes.innerText = sq_pow(str,'sqrt');
                                calcMsg.innerText = calcRes.innerText.replace('Math.sqrt','√');
                };
        };
});

/* %按钮点击事件*/
percent.onclick = () => {
        let num = calcRes.innerText;
        calcRes.innerText = _eval(num/100);
        calcMsg.innerText = num + '/100=';
};

/* 处理JS浮点数运算bug问题 */
let num2Fixed = (res) => {
        let str = typeof(res) === 'number' ? res.toString() : res;
        let ar = str.split('.');
        /* 如果有小数点,需要排除科学计数小数点 */
        if(ar.length <= 1 || str.indexOf('e') > -1) return res;
        if(ar.length >= 14) str = str.slice(0,-1);
        return parseFloat(str);
};

/* 幂&开方运算 */
let sq_pow = (str,operator,arg='') => {
        operator = 'Math.' + operator;
        let regex = /(\(+\))$|(\d+(\.\d+)?)$/g;
        if(regex.test(str)) {
                strRes = str.match(regex);
                str = str.replace(regex, arg ? `${operator}(${strRes},${arg})` : `${operator}(${strRes})`);
        }
        return str;
};

</script>

马黑黑 发表于 2024-1-2 22:53

本帖最后由 马黑黑 于 2024-1-2 23:50 编辑

代码:<style>
#ma { margin: 50px auto 0; padding: 8px; width: fit-content; max-width: 256px; box-sizing: border-box; height: fit-content; border-radius: 10px; background: linear-gradient(to right bottom, #666,tan); box-shadow: 2px 4px 8px black; }
#calcMsg, #calcRes { padding: 6px; color: #eee; font-size: 18px; word-break: break-all; }
#calcRes { text-align: right; color: #000; font-size: 20px; background: #eee; }
#calc { display: grid; place-items: center; grid-template-columns: repeat(4, 60px); grid-template-rows: repeat(5, 50px); }
.btn, .clear { width: 40px; height: 40px; border-radius: 50%; background: #aaa; box-shadow: inset 0 0 10px #000; font: normal 20px/40px sans-serif; text-align: center; user-select: none; cursor: pointer; }
.btn:hover, .clear:hover { background: #ccc; }
.num { color: darkred; font-weight: bold; }
</style>

<div id="ma">
        <div id="calcMsg">计算器</div>
        <div id="calcRes">0</div>
        <div id="calc">
                <div class="clear" id="clear">C</div>
                <div class="btn" data-idx="(">(</div>
                <div class="btn" data-idx=")">)</div>
                <div class="clear" id="del">←</div>
                <div class="btn num" data-idx="7">7</div>
                <div class="btn num" data-idx="8">8</div>
                <div class="btn num" data-idx="9">9</div>
                <div class="btn" data-idx="/">÷</div>
                <div class="btn num" data-idx="4">4</div>
                <div class="btn num" data-idx="5">5</div>
                <div class="btn num" data-idx="6">6</div>
                <div class="btn" data-idx="*">×</div>
                <div class="btn num" data-idx="1">1</div>
                <div class="btn num" data-idx="2">2</div>
                <div class="btn num" data-idx="3">3</div>
                <div class="btn num" data-idx="-">-</div>
                <div class="btn num" data-idx="0">0</div>
                <div class="btn" data-idx=".">.</div>
                <div class="btn" id="percent">%</div>
                <div class="btn" data-idx="+">+</div>
                <div class="clear" id="pow2">x²</div>
                <div class="clear" id="pow3">x³</div>
                <div class="clear" id="sqrt">√</div>
                <div class="clear" id="equal" data-idx="=">=</div>
        </div>
</div>

<script>

let btns = document.querySelectorAll('.btn'); /* 非id运算类按钮 */

/* 运算函数 */
let _eval = (fn) => new Function('return ' + fn)();

/* 处理运算结果 */
let calc = (str) => {
        let res;
        try {
                res = _eval(str);
                res = res == Infinity ? '∞' : num2Fixed(res);
        } catch(err) {
                res = '算式非法';
        }
        return res;
};

/* 非id类按钮点击事件 */
btns.forEach(item => {
        item.onclick = () => {
                if(calcRes.innerText === '0') {
                        if(item.dataset.idx === '0') {
                                calcRes.innerText = '0';
                        }else{
                                calcRes.innerText = isNaN(parseInt(item.dataset.idx)) ? '0' + item.dataset.idx : item.dataset.idx;
                        }
                }else{
                        calcRes.innerText += item.dataset.idx;
                }
                calcMsg.innerText = calcRes.innerText;
        }
});

/* C按钮点击事件 */
clear.onclick = () => {
        calcMsg.innerText = '计算器';
        calcRes.innerText = '0';
};

/* =按钮点击事件 */
equal.onclick = () => {
        let result = calc(calcRes.innerText);
        if(result === '算式非法') {
                calcMsg.innerText = result;
        }else{
                calcRes.innerText = result;
        }
        if(calcMsg.innerText.lastIndexOf('=') < 0) calcMsg.innerText += '=';
};

/* ←按钮点击事件 */
del.onclick = () => {
        calcRes.innerText = calcRes.innerText.slice(0,-1) || 0;
        calcMsg.innerText = calcRes.innerText;
};

/* 开方&幂运算按钮点击事件 */
.forEach(elm => {
        elm.onclick = () => {
                let str = calcRes.innerText;
                switch (elm.id) {
                        case 'pow2':
                                calcRes.innerText = sq_pow(str,'pow','2');
                                calcMsg.innerText = `${str}²`;
                                break;
                        case 'pow3':
                                calcRes.innerText = sq_pow(str,'pow','3');
                                calcMsg.innerText = `${str}³=`;
                                break;
                        default:
                                calcRes.innerText = sq_pow(str,'sqrt');
                                calcMsg.innerText = calcRes.innerText.replace('Math.sqrt','√');
                };
        };
});

/* %按钮点击事件*/
percent.onclick = () => {
        let num = calcRes.innerText;
        calcRes.innerText = _eval(num/100);
        calcMsg.innerText = num + '/100=';
};

/* 处理JS浮点数运算bug问题 */
let num2Fixed = (res) => {
        let str = typeof(res) === 'number' ? res.toString() : res;
        let ar = str.split('.');
        /* 如果有小数点,需要排除科学计数小数点 */
        if(ar.length <= 1 || str.indexOf('e') > -1) return res;
        if(ar.length >= 14) str = str.slice(0,-1);
        return parseFloat(str);
};

/* 幂&开方运算 */
let sq_pow = (str,operator,arg='') => {
        operator = 'Math.' + operator;
        let regex = /(\(+\))$|(\d+(\.\d+)?)$/g;
        if(regex.test(str)) {
                strRes = str.match(regex);
                str = str.replace(regex, arg ? `${operator}(${strRes},${arg})` : `${operator}(${strRes})`);
        }
        return str;
};

</script>

马黑黑 发表于 2024-1-2 22:56

本帖最后由 马黑黑 于 2024-1-2 23:51 编辑

第二版相较于第一版,基于表达式(算式)的运算理念得以进一步落实。例如,四则运算中,将幂运算、平方运算纳入其中,只要算式合法,计算器均能操作。

同时解决一些已经发现的细节逻辑等若干问题。

肯定还会存在其他问题,后续再做修正。

醉美水芙蓉 发表于 2024-1-3 07:12

起个网名好难 发表于 2024-1-3 08:19

{:5_116:}{:5_116:}{:5_116:}

评分和第一版一样,另外
   

期待第三版。

马黑黑 发表于 2024-1-3 08:19

醉美水芙蓉 发表于 2024-1-3 07:12
黑黑老师厉害,发明了计算机!

{:4_170:}肿么可能?

计算器,练练手而已

马黑黑 发表于 2024-1-3 08:30

起个网名好难 发表于 2024-1-3 08:19
评分和第一版一样,另外
   



未对评分机制导致的问题做考虑,现在的精力是处理具体的运算问题以及代码基于运算、操作逻辑等问题。感谢支持。

起个网名好难 发表于 2024-1-3 10:28

本帖最后由 起个网名好难 于 2024-1-3 10:30 编辑

马黑黑 发表于 2024-1-3 08:30
未对评分机制导致的问题做考虑,现在的精力是处理具体的运算问题以及代码基于运算、操作逻辑等问题。感谢 ...
用一对{}把代码包住评分就好了,不一定需要(function() { ... })();

马黑黑 发表于 2024-1-3 12:25

起个网名好难 发表于 2024-1-3 10:28
用一对{}把代码包住评分就好了,不一定需要(function() { ... })();

这个方法挺奇葩

红影 发表于 2024-1-3 12:28

黑黑把正则运算在这个帖子里做了完美的运用,很赞的钻研精神{:4_199:}

红影 发表于 2024-1-3 12:29

这么短的时间已经有了新版本的更新,厉害{:4_187:}

红影 发表于 2024-1-3 12:30

不但功能做了改进,连界面也变得更漂亮了呢{:4_187:}

马黑黑 发表于 2024-1-3 12:31

红影 发表于 2024-1-3 12:30
不但功能做了改进,连界面也变得更漂亮了呢

还好还好

起个网名好难 发表于 2024-1-3 13:14

马黑黑 发表于 2024-1-3 12:25
这个方法挺奇葩

奇葩吗? let 的作用域是块,加上{}后就避免了let重复定义的问题。
从浏览器的调试信息中可以看到这个问题的原因

红影 发表于 2024-1-3 15:39

马黑黑 发表于 2024-1-3 12:31
还好还好

对这个不熟,只能在黑黑的帖子里欣赏一下了{:4_187:}

马黑黑 发表于 2024-1-3 16:29

红影 发表于 2024-1-3 15:39
对这个不熟,只能在黑黑的帖子里欣赏一下了

计算器我倒是有一点点知道的,因为卖猪肉的关系{:4_170:}

马黑黑 发表于 2024-1-3 16:30

红影 发表于 2024-1-3 12:28
黑黑把正则运算在这个帖子里做了完美的运用,很赞的钻研精神

不用正则也是可以的,会啰嗦一点

马黑黑 发表于 2024-1-3 16:31

起个网名好难 发表于 2024-1-3 13:14
奇葩吗? let 的作用域是块,加上{}后就避免了let重复定义的问题。
从浏览器的调试信息中可以看到这个问 ...

嗯。我只是对 {} 的语义问题不解

起个网名好难 发表于 2024-1-3 18:43

马黑黑 发表于 2024-1-3 16:31
嗯。我只是对 {} 的语义问题不解

就是定义个代码块,不知是否确切。

马黑黑 发表于 2024-1-3 19:16

起个网名好难 发表于 2024-1-3 18:43
就是定义个代码块,不知是否确切。

应该是。只是,关于 {} 的官网文档,都没这个用法的。
页: [1] 2 3 4
查看完整版本: 练手:JS计算器(第二版)