JAVASCRIPT 教程 · 第五章

运算符与控制流

这一章讲的是:怎么让程序「做判断、做选择、重复干活」。代码里的英文单词不用会读,把它们当固定写法记住就行,每个符号我都会用中文讲清意思。

1这一章学什么

学完这一章,你要会四件事:用 === 做比较、用 && || ! 做逻辑判断、用 ?? 和 ?. 防止空值出错、会写 if、三元、for 和 for...of,让程序自己决定走哪条路、把一件事重复做很多遍。

2概念

① 比较:判断「等不等、大不大」

===严格相等:值和类型都要一样,才算相等
!==不等于:只要不一样就是 true(真)
>  <大于、小于
>=  <=大于或等于、小于或等于

比较的结果只有两种:true(真,成立)或 false(假,不成立)。

记住一条铁律:永远用 ===,不要用 ==。
== 比较时会偷偷把两边的类型转成一样再比,比如带引号的文字 "1" 和数字 1,用 == 会判成相等。很多莫名其妙的 bug 就是它惹的祸,是出了名的坑。下面「动手试」里你会亲眼看到。
// 端口是不是 443(严格相等)
port === 443
// 端口是不是「不是 443」
port !== 443
// 数量是不是大于 10
count > 10
// 数量是不是大于或等于 10
count >= 10

② 逻辑:与、或、非

&&「与」:两边都为真,结果才为真
||「或」:只要有一边为真,结果就为真
!「非」:取反,真变假、假变真
// 登录了「并且」是会员,才为真
isLogin && isVip
// 登录了「或者」是会员,有一个就为真
isLogin || isVip
// 「没有」登录
!isLogin

还有一个小聪明叫短路:用 && 时,如果前面已经是假,整个结果注定是假,后面就直接不看了。用 || 时前面是真,后面也不看了。程序靠这个省力气,后面写判断时经常用到。

③ 空值:?? 给默认值,?. 防报错

程序里经常遇到「空」:null 和 undefined 都表示「没有值」。处理不好,程序就会报错罢工。这两个符号就是专门防这个的:

const name = input ?? "默认";
// input 是 null 或 undefined 时
// 才用 "默认",有值就用它自己的值

const host = node?.server?.host;
// 一层一层往里读
// 中间任何一层是空的,整体就是 undefined
// 不会报错

要读好几层深的数据(比如订阅配置)时,?. 是必备的:少了它,中间缺一层,程序直接报错崩掉;有了它,最多拿到一个 undefined,还能继续往下走。

④ 算术:加减乘除和两个特殊的

+ - * /加、减、乘、除,和数学里一样
%取余:只要除完剩下的那个数
**乘方:一个数乘自己好几次
// 10 除以 3 余 1,结果是 1
10 % 3
// 2 的 3 次方(2×2×2),结果是 8
2 ** 3

⑤ 条件:if 分情况,三元二选一

if 是「如果……就……,否则如果……,否则……」,适合多种情况挨个判断:

if (port === 443) {
  console.log("是标准端口");
} else if (port > 1000) {
  console.log("高位端口");
} else {
  console.log("其他");
}

从上往下挨个试:哪个条件先成立,就执行哪一段,后面的不再看;全都不成立,就走最后的 else。

如果只是简单的二选一,用三元更短,一行写完:

const label = tls ? "加密" : "明文";
// tls 是真,就取 "加密"
// tls 是假,就取 "明文"

⑥ 循环:一件事重复做很多遍

写法一:for,按下标一个一个取。i 从 0 开始数,每轮加 1,到 list.length(数组长度)为止。

for (let i = 0; i < list.length; i++) {
  console.log(list[i]);
}

写法二:for...of,直接遍历每个元素。不用管下标,最省心,遍历数组时最常用这种:

for (const item of list) {
  console.log(item);
}

写法三:forEach,自带下标。既要元素、又想要它是第几个时用它:

list.forEach((item, i) => {
  console.log(i, item);
});
// i 是下标(从 0 开始)
// item 是元素本身

写法四:while,条件成立就一直转。不知道要转多少圈、只知道「什么时候停」时用它,注意别忘了让条件有机会变假,不然会死循环:

while (count > 0) {
  count--;
  // 每轮减 1,减到 0 就停
}

3动手试

下面的代码可以直接改,点「运行」就能在页面里真的跑一遍,看结果对不对。改错了也不怕,出错会告诉你错在哪一类。

试一试 1:== 和 === 的区别

同样是比较「文字 1」和「数字 1」,看两个结果为什么不一样。

第一个是 true,第二个是 false,坑就坑在这

试一试 2:?? 和 ?. 怎么防空

一个给默认值,一个读空对象也不报错。

不会报错,会打印出「默认」和 undefined

试一试 3:三元 + for...of 遍历地区

先用三元做个二选一,再用 for...of 把节点地区一个个打印出来。

试试把 tls 改成 false 再运行一次

4练一练

5 道选择题,点一下选项就知道对错,错了会告诉你错在哪。

已答对 0 / 5
5 道题都答完了,这一章的核心你已经摸清了。

5适用案例

案例一 · 判断端口

port === 443,判断是不是标准端口

443 是加密网页(https)的标准端口。Worker 里判断请求该往哪条路走、检查节点配置对不对,第一步往往就是拿端口和 443 严格比较——用 === 而不是 ==,就是怕类型被偷偷转了之后误判。

if (port === 443) {
  console.log("是标准端口");
}
案例二 · 读多层数据

?. 读订阅配置,缺一层也不崩

订阅配置是一层套一层的数据:节点下面有 server,server 下面才有 host。有的节点写法不全,中间可能缺一层。用 ?. 读,缺了就拿到 undefined,后面再给个默认值,程序不会当场报错停摆。

const host =
  node?.server?.host ?? "未知";
// 中间缺任何一层都不报错
// 最后兜底给 "未知"
案例三 · 循环检查

for...of 遍历列表,逐个检查到期

tg-bot 的订阅到期提醒就是这个套路:把所有订阅拿出来,用 for...of 一个一个过,发现哪个快到期了就发提醒。列表里有 5 个就查 5 遍,有 100 个就查 100 遍,代码一行都不用多写。

for (const sub of subs) {
  if (sub.daysLeft <= 3) {
    console.log(sub.name + " 快到期了");
  }
}

✓小结

一句话记住这一章:比较永远用 ===,空值用 ?? 和 ?. 兜底,判断用 if 和三元,重复的事交给 for...of。