1.值与数据类型:值也有种类
第1讲里我们用console.log输出过数字和文字。程序处理的每一份数据叫作值,值的种类叫作数据类型。人能根据上下文区分“3”这个字和3这个数,计算机则给每个值都标上种类,并按种类决定能对它做什么。数可以相加、相乘,文字可以拼接、可以数长度。
JavaScript里最常用的基本数据类型有五种。数字(number)不区分整数和小数,全部当作同一种类处理。字符串(string)是用引号括起来的文字。布尔值(boolean)只有真(true)和假(false)两个值,在第3讲的条件语句中起核心作用。undefined表示“还没有确定值”,null表示“有意标明没有值”。此外还有表示极大整数的bigint和表示独一无二标记的symbol,但本课程几乎不会用到。
值的种类可以用typeof运算符查看,结果是装着类型名称的字符串。有一个由来已久的例外:typeof null得到的不是"null",而是"object"。这是JavaScript早期设计留下的痕迹,如今改掉会让现有网页出问题,所以就保留了下来。因此,要判断某个值是不是null时,不用typeof,而用第3讲要学的=== null比较。
console.log(typeof 42, typeof 3.14);
console.log(typeof "你好", typeof "42");
console.log(typeof true, typeof undefined);
console.log(typeof null); // 由来已久的例外number number string string boolean undefined object
| 数据类型 | 例子 | `typeof`结果 | 用途 |
|---|---|---|---|
| 数字 | 42, 3.14, -7 | "number" | 个数、金额、分数等计算 |
| 字符串 | "小明", '42' | "string" | 姓名、句子、电话号码这类不用来计算的文字 |
| 布尔值 | true, false | "boolean" | 是/否、条件判断 |
undefined | undefined | "undefined" | 还没有放入值的状态 |
null | null | "object"(例外) | 有意标明没有值 |
2.变量:用`let`和`const`贴上名字标签
同一个值要用好几次,或者要暂时保存计算结果,就得给值起个名字。这个名字叫作变量。与其说变量是箱子,不如说它更像贴在值上的名字标签。写const price = 1200;,就给1200这个值贴上了price这个标签,此后只写price也指的是1200。这里的=不是数学里的“等于”,而是“把右边的值放进左边的名字”。
创建变量的方法有两种。const是一旦定下就不能再放入别的值的变量,let是之后可以换成别的值的变量。好习惯是默认用const,只在值需要改变时(累加合计、计数等)才用let。这样代码里就写明了“这个值不会变”,读代码的人可以放心;万一不小心覆盖了,也会立刻报错提醒。旧代码里常见的var是规则容易混淆的老写法,新代码里不用。
名字要起得让人看出里面装的是什么。比起a、x2,price、totalCount好读得多。在JavaScript里,几个单词连在一起时,通常从第二个单词起首字母大写(totalPrice)。名字不能以数字开头,不能含空格,也不能用let、if这类保留字。名字区分大小写,所以price和Price是两个不同的变量。
const,会变的值用letconst price = 1200;
const count = 3;
const total = price * count;
console.log("合计:", total);
let visits = 0;
visits = visits + 1;
visits = visits + 1;
console.log("访问次数:", visits);合计: 3600 访问次数: 2
const变量重新赋值,会在第3行报错停下const rate = 0.05;
console.log("利率:", rate);
rate = 0.07;
console.log("新利率:", rate);利率: 0.05
TypeError: Assignment to constant variable.undefinedlet memo;
console.log(memo);
memo = "买菜";
console.log(memo);undefined 买菜
visits = visits + 1说不通;但在代码里它的意思是“把当前visits的值加1,再把结果放回visits”。也可以简写成visits += 1。第4讲的循环会一直用到这种“累加”模式。3.运算符:计算与拼接
数字除了四则运算+、-、*、/之外,还有求余数的%和求幂的**。%比想象中用得更多。一个数除以2的余数是0就是偶数;把以分钟计的时间除以60,用商和余数就能得到“几小时几分”。运算顺序和数学一样,乘除先于加减;拿不准时最好加上括号,把意图写清楚。
即使除法结果不是整数,JavaScript也会给出小数答案。7 / 2是3.5。只要商的话,用Math.floor舍去小数部分;四舍五入用Math.round,向上取整用Math.ceil。除以0不会报错,而是得到Infinity(无穷大);做无法变成数字的计算,会得到表示“不是数字”的NaN。还要记住,NaN虽然叫这个名字,typeof的结果却是"number",它是一个特殊的数字值。
+也能用在字符串上。两个字符串用+相加就是拼接,"你" + "好"会变成"你好"。要把变量的值放进字符串里,用反引号(键盘上数字1左边的键)括起来、在里面用美元符号加花括号放入变量的“模板字符串”很方便。具体写法请在下面的代码示例里亲眼看一看。第7讲会更详细地讲字符串。
%是余数,**是求幂console.log(7 + 2, 7 - 2, 7 * 2, 7 / 2);
console.log(7 % 2, 2 ** 10);
console.log(Math.floor(7 / 2), Math.round(2.5));
console.log(Math.ceil(2.1), 1 / 0);9 5 14 3.5 1 1024 3 3 3 Infinity
const minutes = 135;
const h = Math.floor(minutes / 60);
const m = minutes % 60;
console.log(h + "小时" + m + "分");
console.log(`${h}小时${m}分`);2小时15分 2小时15分
4.类型转换的陷阱:`"1" + 2`为什么是`"12"`
JavaScript在混合不同种类的值做计算时,不会报错,而是自作主张把其中一方转换后再算。这叫作隐式类型转换。它有时很方便,却是初学者出错的一大原因。最有名的例子是"1" + 2。+的一边是字符串时,JavaScript会把它理解为“拼接”,把数字2变成字符串"2",结果就成了"12"。
可是"5" - 2却是3。因为-、*、/是不能用在字符串上的运算,这次会把字符串转成数字来算。同样是字符串和数字,换个运算符,结果有时是字符串、有时是数字,难免让人糊涂。这个问题在实际中经常发生。从输入框、文本文件、CSV读进来的值,看起来像数字,其实大多是字符串,直接相加得到的不是合计,而是一串连在一起的文字(第9讲)。
解决办法是在计算之前亲手把它转成数字。这叫作显式类型转换。Number("12")会变成数字12;像Number("12韩元")这样混入了不能读成数字的文字,就会得到NaN。反过来,把数字转成字符串用String(12)。结果不对劲时,先养成用typeof打印值的种类的习惯。第8讲的调试,也是从这样确认“值是不是我以为的那种类型”开始的。
+的一边是字符串时就变成拼接console.log("1" + 2);
console.log(1 + 2 + "3");
console.log("5" - 2, "5" * "2");
console.log(typeof ("1" + 2));12 33 3 10 string
Number转换再计算const a = "3000";
const b = "4500";
console.log(a + b);
console.log(Number(a) + Number(b));
console.log(Number("12韩元"), Number(""));
console.log(String(12) + String(3));30004500 7500 NaN 0 123
- 加完之后结果长得奇怪,就要怀疑是不是字符串被拼接在了一起
- 从外部读进来的值(输入、文件、表格)在计算前先用
Number转换 Number的结果是NaN,说明混进了不是数字的文字(单位、逗号等)- 注意空字符串
""用Number转换后会变成0——要决定空格子算作0还是另行处理
5.`0.1 + 0.2`不等于`0.3`
在练习场里输出0.1 + 0.2,会得到0.30000000000000004。这不是JavaScript不会算数。计算机用二进制存储数,十进制的0.1在二进制里是无限延续的小数,放进固定的位数时会被截掉一点点。这和十进制里无法把1/3写成0.333…写到尽头是同一个道理。几乎所有编程语言都遵循同一个标准来实现这种方式(浮点数),所以在别的语言里也会一模一样地发生。
误差非常小,在大多数计算中不成问题,但有两种情况要小心。第一是比较是否相等时。0.1 + 0.2 === 0.3是false。第二是像金额这样差一分钱都不行的计算。小数多次相加、相乘,小误差会累积起来,有可能在显示时露出来。
常用的解决办法有三种。换成韩元、美分这类最小单位的整数来计算,只要是整数之间的加、减、乘,就不会产生误差(除非数非常大);涉及除法时,只在最后四舍五入一次。只在显示到屏幕上时才用toFixed统一位数,或用Math.round四舍五入。比较时则看两个值的差是否足够小。练习场的评分器对小数结果也会把极小的差视为相等。不过别忘了,toFixed的结果不是数字,而是字符串。
toFixed返回的是字符串console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
console.log((0.1 + 0.2).toFixed(2));
console.log(typeof (0.1 + 0.2).toFixed(2));0.30000000000000004 false 0.30 string
// 1.10美元 + 2.20美元,换成美分(整数)计算
const cents = 110 + 220;
console.log(cents / 100);
console.log(1.1 + 2.2);3.3 3.3000000000000003
const total = 1200 * 3;
const pay = Math.round(total * 90 / 100);
console.log("实付金额:", pay);实付金额: 3240
- 第1步 合计是整数计算,很安全:1200 * 3 = 3600。
- 第2步 不把折扣率写成小数(0.9)去乘,而是换成整数的乘法和除法:3600 * 90 / 100 = 3240。
- 第3步 有可能除不尽,所以按韩元单位套上
Math.round(或者按规定用Math.floor)。 - 验算:3600的10%是360,3600 − 360 = 3240韩元,结果一致。
6.`null`与`undefined`:“空”的两副面孔
JavaScript里有两个表示“没有值”的值。undefined通常是JavaScript自己放进去的值:创建了变量却没放入值时、读取对象里不存在的属性时(第6讲)、函数什么都不返回时(第5讲),就会出现它。null是人有意放进去的值,表示“我知道这里是空的”。比如在还没填写的电话号码栏里先放上null。
两者用在计算里都会得出奇怪的结果。null在数字计算中被当作0,所以null + 1是1;undefined无法转成数字,所以undefined + 1是NaN。这类结果不带任何错误信息、悄无声息地扩散开来,因此更危险。如果合计变成了NaN,就可以把它看作计算过程中某处混进了undefined的信号。
更大的问题出在想从空值里取东西的时候。试图读取null或undefined的属性,会因TypeError而停下。这也是AI写的代码里常见的错误,原因是没有处理“数据为空时”的情况。第3讲会学习用条件语句提前把这类情况筛掉。
console.log(null + 1, undefined + 1);
let phone = null;
console.log("电话号码:", phone);
console.log(phone.length);1 NaN
电话号码: null
TypeError: Cannot read properties of null (reading 'length')📌 要点总结
- 基本数据类型有数字、字符串、布尔值、
undefined、null,用typeof查看(typeof null是例外,得到"object") - 默认用
const,只在值需要改变时用let,不用var +的一边是字符串时就变成拼接——读进来的值先用Number转换再计算0.1 + 0.2的误差源于二进制存储;金额按整数单位计算,显示时再四舍五入- 看到
NaN或TypeError,就怀疑计算中是否混进了undefined、null
🧪 代码练习
在函数里填空的练习。函数会在第5讲详细学习,现在只要知道“用括号里的名字接收值,返回return后面的值”就够了。
代码只在本浏览器内的隔离环境中运行,不会发送到服务器。无法访问网络,超过2秒会自动停止。改过的代码只保存在此浏览器中。按 Ctrl+Enter(⌘+Enter)运行,Tab 键输入两个空格(先按 Esc 再按 Tab 可跳到下一项)。
浏览器脚本未开启,无法运行代码,但题目、初始代码、自动检查项和参考答案都可以直接阅读。
1求合计
totalPrice(price, count)接收价格price和数量count(都是数字),返回合计。数量为0时返回0。
totalPrice(1200, 3)期望值3600totalPrice(2500, 1)期望值2500totalPrice(990, 0)期望值0totalPrice(0, 5)期望值0
💡 提示
乘号是*。const total = price * count;
查看参考答案
function totalPrice(price, count) {
const total = price * count;
return total;
}
2把字符串形式的数字相加
addStrings(a, b)接收两个写着数字的字符串(例如"3000"、"4500"),以数字形式返回两数之和。注意,直接a + b会拼接在一起。
addStrings("3000", "4500")期望值7500addStrings("1", "2")期望值3addStrings("0", "0")期望值0addStrings("-5", "2.5")期望值-2.5
💡 提示
Number(a)会把字符串a转成数字。
查看参考答案
function addStrings(a, b) {
return Number(a) + Number(b);
}
3告诉我数据类型的名称
typeName(v)以字符串形式返回值v的数据类型名称。它和typeof一样,只是在null时返回"null",而不是"object"。
typeName(42)期望值"number"typeName("42")期望值"string"typeName(true)期望值"boolean"typeName(null)期望值"null"typeName("")期望值"string"
💡 提示
在return typeof v;前面加上if (v === null) { return "null"; }。if会在第3讲详细学习。
查看参考答案
function typeName(v) {
if (v === null) {
return "null";
}
return typeof v;
}
4AA制金额
splitBill(total, people)返回总额total韩元由people人分摊时每人应付的金额。为了不少付,按10韩元为单位向上取整。例:10000韩元由3人分摊,3333.33…韩元 → 3340韩元。
splitBill(10000, 3)期望值3340splitBill(9000, 3)期望值3000splitBill(25010, 2)期望值12510splitBill(0, 4)期望值0splitBill(1, 1)期望值10
💡 提示
先除以10,用Math.ceil向上取整,再乘以10。本来就是10韩元整数倍的金额会保持不变。
查看参考答案
function splitBill(total, people) {
const each = total / people;
return Math.ceil(each / 10) * 10;
}
🤖 可以这样问 AI
复制后把 [ ] 部分换成你的情况。不要直接相信回答,请与正文对照核实。
计算结果不对劲时
下面这段JavaScript代码的结果不是[期望的值],而是[实际的值]。请用表格列出每一行中各个变量的数据类型(`typeof`)如何变化,并指出发生类型转换的地方。改好的代码放在最后给我。代码:[粘贴代码]
想练习对类型转换的感觉时
请出8道像`"1" + 2`、`"5" - 2`、`null + 1`这样的JavaScript类型转换题。先把答案藏起来,等我发来预测的值后,告诉我对不对以及原因。最后2道题请做成实际工作的样子(把从表格里读出的金额相加)。
审查金额计算代码时
请审查下面这段JavaScript金额计算代码,看哪里可能出现小数误差或取整问题。如果改成按整数单位(如韩元)计算会是什么样子,请写出来,并在注释里说明用的是四舍五入还是舍去。代码:[粘贴代码]
🧰 相关工具
可以当场检查、整理本讲涉及的 JSON、正则表达式和文本的工具。请不要粘贴真实的个人信息或密码。
- MDN Web Docs — JavaScript指南(Guide)中的语法与数据类型、表达式与运算符
- MDN Web Docs — 关于typeof、Number、浮点数的参考文档
- 关于IEEE 754浮点数标准的一般性说明
达成全部学习目标后,请点击完成。
此浏览器无法保存记录,仅在当前页面显示。