B 币安 · 全球最大的加密货币交易所币安 注册 → AD OKX OKX · 全球领先的加密货币交易所OKX 注册 → AD
💻 编程基础 · 第2讲 / 10

值、变量与数据类型:给值贴上名字标签

程序就是不断接收值、改变值、交出值的过程。值有种类(数据类型),变量则是贴在值上的名字标签。混用不同种类的值时发生的自动转换,是初学者出错的一大来源。

⏱ 约20分钟 ✍️ 练习题 5 道 🧪 代码练习 4 题 更新 2026-10-09
🎯 本讲学习目标
  • 能区分数字、字符串、布尔值、null、undefined,并用typeof查看
  • 知道let和const的区别,能恰当地选择并创建变量
  • 会用算术运算符和字符串拼接,能预测"1" + 2这类自动类型转换的结果
  • 能说明0.1 + 0.2产生误差的原因,并在金额计算中使用避开误差的方法

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比较。

用引号括起来的"42"不是数字,而是字符串
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"是/否、条件判断
undefinedundefined"undefined"还没有放入值的状态
nullnull"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,会变的值用let
const 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.
没有放入值就创建的变量是undefined
let 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
例题这是一个虚构的例子。买了3件1,200韩元的商品,享受九折优惠。要想没有小数误差地求出以韩元为单位的实付金额,该怎么计算?
const total = 1200 * 3;
const pay = Math.round(total * 90 / 100);
console.log("实付金额:", pay);
输出
实付金额: 3240
  1. 第1步 合计是整数计算,很安全:1200 * 3 = 3600。
  2. 第2步 不把折扣率写成小数(0.9)去乘,而是换成整数的乘法和除法:3600 * 90 / 100 = 3240。
  3. 第3步 有可能除不尽,所以按韩元单位套上Math.round(或者按规定用Math.floor)。
  4. 验算:3600的10%是360,3600 − 360 = 3240韩元,结果一致。
答案3,240韩元(用整数计算后,按韩元单位四舍五入)

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

✍️ 练习题

请先自己作答,再展开“答案与解析”。

Q1. console.log("1" + 2 + 3);的输出是?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ② 123

从左往右计算。"1" + 2变成字符串"12","12" + 3也是拼接,得到"123"。反过来,1 + 2 + "3"先算出3再拼接,得到"33"。

Q2. 下面哪个变量不适合用const创建?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ② 在循环中不断累加的合计

累加的合计值会不断变化,所以要用let创建。给const重新赋值会出现TypeError。

Q3. typeof null的结果是?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ③ "object"

这是早期设计留下的痕迹,结果是"object"。所以判断是否为null时,用=== null比较。

Q4. 要把价格字符串"3000"和"4500"相加得到7500,合适的代码是?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ③ Number("3000") + Number("4500")

必须先把两者各自转成数字再相加。第二个选项先拼接成"30004500"再转成数字,结果是30004500。

Q5. 请用一句话说明0.1 + 0.2 === 0.3为什么是false。

答案与解析
参考答案 因为计算机用二进制存储数,而0.1和0.2无法用二进制精确表示,会产生非常小的误差

结果是0.30000000000000004。金额计算要按韩元、美分这样的整数单位进行,比较时看差是否足够小。

🧪 代码练习

在函数里填空的练习。函数会在第5讲详细学习,现在只要知道“用括号里的名字接收值,返回return后面的值”就够了。

代码只在本浏览器内的隔离环境中运行,不会发送到服务器。无法访问网络,超过2秒会自动停止。改过的代码只保存在此浏览器中。按 Ctrl+Enter(⌘+Enter)运行,Tab 键输入两个空格(先按 Esc 再按 Tab 可跳到下一项)。

浏览器脚本未开启,无法运行代码,但题目、初始代码、自动检查项和参考答案都可以直接阅读。

1求合计

totalPrice(price, count)接收价格price和数量count(都是数字),返回合计。数量为0时返回0。

自动检查
  • totalPrice(1200, 3)期望值 3600
  • totalPrice(2500, 1)期望值 2500
  • totalPrice(990, 0)期望值 0
  • totalPrice(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")期望值 7500
  • addStrings("1", "2")期望值 3
  • addStrings("0", "0")期望值 0
  • addStrings("-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)期望值 3340
  • splitBill(9000, 3)期望值 3000
  • splitBill(25010, 2)期望值 12510
  • splitBill(0, 4)期望值 0
  • splitBill(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浮点数标准的一般性说明

达成全部学习目标后,请点击完成。

💻 编程基础

  1. 1什么是程序:把事情拆成顺序、条件和循环的思维
  2. 2值、变量与数据类型:给值贴上名字标签
  3. 3条件语句:根据情况走不同的路
  4. 4循环 — 让同一件事准确地重复多次
  5. 5函数 — 拆成一个个有名字的小机器
  6. 6数组与对象 — 装载和处理数据
  7. 7字符串与文本处理——截取、查找、替换文字
  8. 8调试与读懂错误——把红字变成线索
  9. 9简单的自动化示例——用代码做表格计算和文本整理
  10. 10阅读并检验 AI 写的代码——测试、安全、许可证与个人信息
📚 推荐阅读
📊Using AI for Spreadsheets, and Checking the Formulas→ 💻AI Coding Assistants: Security and Licence Risks→ 💬Practising Conversation in a Foreign Language with AI→ ⚙️Finding Work Tasks Worth Automating with AI→
← AI时代基础学力课程