欢迎光临
我们一直在努力

04 数字与字符串:+ 号的两副面孔(拼接与相加 · NaN · 类型互转 · 模板字符串)

上一篇:03 变量与数据类型:给数据贴标签(let / const · 字符串、数字、布尔 · typeof)


上篇练习参考答案

练习 1(手动模拟):手动模拟(先写答案再回车):typeof “true”、typeof typeof 1、console.log(28 + " 岁") 各输出什么? typeof "true" 得 "string"——引号包着就是文字,哪怕内容长得像布尔值;typeof typeof 1 得 "string"——检查员递来的是写着"number"的纸条,纸条本身是文字;28 + " 岁" 得 "28 岁"——+ 见到字符串就切拼接,28 被拉进文字世界。 在这里插入图片描述

练习 2(预测再验证):预测再验证:把实战代码里 const birthYear 改成 let birthYear,文件末尾加一行 birthYear = 1999;——运行结果和原来有什么不同?const 和 let 差的到底是什么? 输出和原来几乎完全一样。const/let 差的不是"值能不能用",是"标签能不能重新赋值"——const 版本会在 birthYear = 1999 那行当场炸出 TypeError。最好的错误是还没跑就被拦下的错误:报错拦你,是在救你。

练习 3(改造要点):改造要点:给资料卡加一条"爱好"数据(提示:数据区、代码区、体检区三个位置各动一行); 三个位置各动一行

const hobby = "写代码和摸鱼"; // 数据区
console.log(`爱好:${hobby}`); // 代码区
console.log(`hobby 的类型:${typeof hobby}`); // 体检区

在这里插入图片描述

练习 4(疫苗揭晓):在控制台分别执行 console.log(1 + 1) 和 console.log(“1” + 1)——先别回车,把两个答案写在纸上再运行。如果猜得不全对,第 4 篇会讲清这两个结果背后同一套规则,以及它在真实项目里如何把"合计 640"悄悄变成"198225217" 1 + 1 得 2,"1" + 1 得 "11"。规则:+ 号两边只要有一边是字符串,就切成拼接模式,另一边被悄悄转成文字。数字一旦被拉进文字世界,后面的 + 全是拼接。你写的是合计,它执行的是串门——这就是本篇要拆的雷。 在这里插入图片描述


一、场景引入:一起真实的"账单事故"

在这里插入图片描述

上篇你学会了贴标签,本篇拆 JS 民间第一事故。

团建结束你来记账,从群里复制来三笔费用:"198元"、"225元"、"217元"。看着像数字?它们是带单位的字符串——从表格、网页、输入框复制来的"数字",十有八九是字符串(输入框产出的永远是字符串,16 篇会再撞见一次)。

于是事故发生:

const total = "198元" + "225元" + "217元";
console.log(total); // "198元225元217元"——三笔钱串成了一串门牌号

在这里插入图片描述

没有任何报错,控制台安静地交出一串鬼话——这就是 02 篇说过的"静默失败"在数据界的形态,比报错可怕十倍。本篇四件事:说清 + 的规则、学会把文字洗成数字、认识 NaN、用模板字符串优雅说话。

二、本篇目标

  • 一句话说清 + 的判定规则,任意 x + y 能先纸上预测再验证;
  • 会用 Number / parseInt / parseFloat 把文字洗成数字,知道各自分工;
  • 认识 NaN 的两个怪脾气,会正确检测它;
  • 掌握模板字符串,从此告别引号套引号。
  • 三、知识点讲解

    3.1 + 号的两副面孔:算术模式与拼接模式

    ‘+’ 是 JS 里唯一"身兼两职"的运算符,切换规则就一句: 两边都是数字 → 相加;任意一边是字符串 → 拼接(另一边被隐式转成文字)

    控制台四连测(先猜后跑):

    console.log(1 + 1); // 2 纯数字:算术
    console.log("1" + 1); // "11" 有文字:拼接,1 被拉进文字世界
    console.log(1 + 1 + "1"); // "21" 从左往右算:先 1+1=2,再 2+"1"
    console.log("1" + 1 + 1); // "111" 先 "1"+1="11",从此全拼接

    在这里插入图片描述

    第三、四条是精髓:运算从左往右,一旦中途某步进了文字世界,后面全部沦陷。所以 "1" + 1 + 1 和 1 + 1 + "1" 结果不同——顺序决定命运。

    顺带一个大新闻:减、乘、除没有两副面孔:

    console.log("12" 8); // 4 减号走数学:字符串被隐式转成数字
    console.log("3" * "4"); // 12 乘除同上
    console.log("12" / "4"); // 3 乘除同上

    在这里插入图片描述

    于是老江湖有句黑话叫 "12" – 0 转数字。认识即可,别学——隐式转换是"赌浏览器猜对你的心思",显式转换才是"明说"。赌赢一百次,输的那一次会让你在客户面前社死。

    3.2 显式转换:把文字洗成数字的三把刷子

    Number:严格派

    Number("18") // 18
    Number("3.5") // 3.5
    Number(" 18 ") // 18 前后空格能容忍
    Number("18元") // NaN 混进任何杂质:拒绝
    Number("") // 0 空串算 0(历史决定,记住就好)

    在这里插入图片描述

    parseInt / parseFloat:实干派——从左往右薅数字,遇到第一个非数字字符就停,杂质直接丢弃:

    parseInt("198元") // 198 带单位的账单,直接薅出数字
    parseFloat("60.5元") // 60.5 parseFloat 保留小数
    parseInt("12.9") // 12 parseInt 只取整数,小数直接砍
    parseInt("元198") // NaN 开头就不是数字:没得薅

    在这里插入图片描述

    从网页、群聊、表格里扒带单位的数字,parseInt 一族是神器。选型口诀:

    纯数字串用 Number(严格),带单位带杂质用 parseInt / parseFloat(实干)。

    反方向(数字转文字)两个顺手工具:String(18) 得 "18";金额显示专用 (640).toFixed(2) 得 "640.00"——注意 toFixed 返回的是字符串:它是化妆师不是数学家,用来显示,不能拿来再参与计算。 在这里插入图片描述

    3.3 NaN:数字世界的传染病人

    Number 洗不动的数据不会报错,会变成 NaN(Not a Number)。三个必知:

    typeof NaN // "number" NaN 是 number 类型却"不是数字"(历史包袱,别较真)
    NaN + 5 // NaN 特性一:传染,沾上 NaN 的运算全是 NaN
    NaN === NaN // false 特性二:全 JS 唯一不等于自己的值

    在这里插入图片描述

    特性一解释了它的可怕:账单里出现一个 NaN,说明上游某笔数据脏了——它不拦你,只是让结果一路烂到底。特性二是个大坑:想检测 NaN 写 x === NaN 永远是 false(它连自己都不认)。正确姿势:

    Number.isNaN(NaN) // true 才是 NaN

    在这里插入图片描述

    排查 NaN 的标准流程也简单:沿着计算链逐个 console.log(typeof x, x),第一个变 NaN 的就是病灶——11 篇会把这套"沿链排查"升级成专业调试术。 在这里插入图片描述

    3.4 模板字符串:拼接的优雅时代

    数字洗好了,最后一环是"说人话"。老式拼接你已经领教过:

    console.log("人均 " + per + " 元,共 " + people + " 人,合计 " + total + " 元");

    引号和加号排长队,漏一个引号能让你排查半小时。现代 JS 的正解——模板字符串:一对反引号(键盘 Esc 下方、Tab 上方那个键 `)包住整句话,变量用 ${} 塞进去:

    console.log(`人均 ${per} 元,共 ${people} 人,合计 ${total}`);

    三大优点,各值回票价:

  • 引号只剩一对——引号配对错误的温床直接拆除;
  • 可以换行——反引号里直接敲回车就是真实换行(+ 拼接想换行要塞 \\n);
  • ${} 里能放表达式——${total / people} 现场算,不用先存变量。
  • 03 篇你已经照抄过它,本篇正式转正:从本篇起,本专栏所有输出默认模板字符串。

    四、完整实战:团建账单分摊器

    把四件事拼成一条流水线:脏数据 → 洗 → 算 → 说人话——这条流水线以后会在表单、爬虫、看板里反复出现。新建 账单分摊器.html(点击html名下载):

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <title>团建账单分摊器</title>
    </head>
    <body>
    <h2>团建账单分摊器</h2>
    <p>账单报告在控制台——按 F12 查看。</p>

    <script>
    // ===== 数据区:群里复制来的原始账单(全是带单位的字符串)=====
    const bill1 = "198元"; // 午餐
    const bill2 = "225元"; // 晚餐
    const bill3 = "217元"; // 唱歌
    const drinks = "60.5元"; // 饮料
    const people = 8; // 人数(自己数的,是真数字)

    // ===== 第一步:洗(带单位 → parseFloat 薅出数值)=====
    const food1 = parseFloat(bill1);
    const food2 = parseFloat(bill2);
    const food3 = parseFloat(bill3);
    const drinkCost = parseFloat(drinks);

    // 不放心就体检:洗完是什么类型?
    console.log(`洗后体检:${typeof food1} ${food1}${typeof drinkCost} ${drinkCost}`);

    // ===== 第二步:算 =====
    const foodTotal = food1 + food2 + food3;
    const total = foodTotal + drinkCost;
    const perPerson = total / people;

    // ===== 第三步:说人话(模板字符串 + toFixed)=====
    console.log("=".repeat(40));
    console.log(" 团 建 账 单");
    console.log("=".repeat(40));
    console.log(`人数:${people}`);
    console.log(`餐费三笔:${food1} + ${food2} + ${food3} = ${foodTotal}`);
    console.log(`含饮料总计:${total.toFixed(2)}`);
    console.log(`人均全含:${perPerson.toFixed(2)}`);
    console.log(`每人整数收款:${Math.round(perPerson)} 元(四舍五入)`);
    console.log("=".repeat(40));

    // ===== 事故现场还原:不洗直接加会怎样 =====
    const dirtyTotal = bill1 + bill2 + bill3;
    console.log(`事故还原——不洗直接加:${dirtyTotal}`);
    console.log(`类型对比——洗前 ${typeof bill1},洗后 ${typeof food1}`);
    </script>
    </body>
    </html>

    注意 Math.round(perPerson)——Math 是 JS 自带的数学工具箱,round 负责四舍五入,先混脸熟,用到再讲。

    五、运行效果

    在这里插入图片描述

    洗后体检:number 198,number 60.5
    ========================================
    团 建 账 单
    ========================================
    人数:8 人
    餐费三笔:198 + 225 + 217 = 640 元
    含饮料总计:700.50 元
    人均全含:87.56 元
    每人整数收款:88 元(四舍五入)
    ========================================
    事故还原——不洗直接加:198元225元217元
    类型对比——洗前 string,洗后 number

    六、常见报错与排查

    现象 1:合计变成"198元225元217元"一长串(没有任何报错)

    原因:+ 的遇文则文——加的全是字符串。 解决:计算前先洗,parseFloat / Number 处理一遍再参与运算。这是 JS 数据处理的第一反射:拿到外部数据,先问一句"你真的是数字吗"。

    现象 2:金额输出 NaN

    原因:某笔数据 Number 洗不动(比如 "元198" 开头就是杂质),NaN 一路传染到结果。 解决:沿计算链逐个 console.log(typeof x, x),第一个 NaN 就是病灶;修数据或改用 parseInt。检测一律 Number.isNaN(x),别写 x === NaN(永远 false)。

    现象 3:页面上显示出一串 ${perPerson} 原文

    原因:模板字符串忘了用反引号,用了普通引号——普通引号里的 ${} 没有魔法,原样输出。 解决:整句话最外层换成反引号。以后看到页面出现 ${ 字样,直接去查引号类型。

    排查心法:结果不对先查类型;金额 NaN 沿链体检;出现 ${ 检查反引号。数据问题九成是类型问题——typeof 是你唯一的听诊器。

    七、练习作业

  • 手动模拟(先写答案再回车):"10" + 5、10 + 5 + "5"、"10" – 5、"3" * "4" 各得什么?
  • 预测再验证:Number("")、Number("12 8")、parseInt("12 8")、parseFloat("3.5折") 的结果各是什么?哪个出乎你意料?
  • 改造要点:账单加第四笔餐费 "128元"(烧烤)——数据区加一行、合计处加一项,重跑;顺便观察"事故还原"那行跟着变长了多少;
  • 疫苗(下篇预告):人均 87.56 元,预算线 90 元——"超没超"是个是非题,程序靠什么回答是非题?控制台试试 console.log(87.56 > 90);再试试 console.log("10" < "9")——结果竟然是 true,10 为什么会小于 9?05 篇揭晓比较运算符的坑,和 JS 世界最重要的三条等号。

  • 下一篇预告:《05 条件判断:=== 才是正道》——程序怎么回答"是/否"问题?为什么永远不要用两个等号做比较?if/else、三元表达式、switch 一次讲齐。

    赞(0)
    未经允许不得转载:171主机测评 » 04 数字与字符串:+ 号的两副面孔(拼接与相加 · NaN · 类型互转 · 模板字符串)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址