You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
30-Days-Of-JavaScript/js_notes/03-operators.js

199 lines
12 KiB

// 03 · 运算符
// 学练复习一体:概念总结 → 示范 → 轮到你
// 跑法:Quokka / 右上角 ▷ / node 本文件
// 示范区每行后注释是【预期输出】,跑完自己对答案
// ═════ 概念总结 ═════
//
// 1. 算术运算符
// ┌──────┬────────────┬──────────┬──────────────────────────┐
// │ 符号 │ 含义 │ 例子 │ 结果 │
// ├──────┼────────────┼──────────┼──────────────────────────┤
// │ + │ 加 │ 2 + 3 │ 5 │
// │ - │ 减 │ 5 - 2 │ 3 │
// │ * │ 乘 │ 4 * 3 │ 12 │
// │ / │ 除 │ 10 / 4 │ 2.5(JS 除法不取整!) │
// │ % │ 取余(模) │ 10 % 3 │ 1 │
// │ ** │ 幂(次方) │ 2 ** 3 │ 8 │
// └──────┴────────────┴──────────┴──────────────────────────┘
// 两个初学重点:
// - JS 里 10 / 4 = 2.5,不像有些语言整数相除会取整。
// - % 取余超常用:判断奇偶(n % 2 === 0 就是偶数)、循环里做周期。
//
// ⚠️ 负数取余的坑:判奇偶要用 === 0 不要用 === 1
// JS 里取余结果的符号跟着"被除数"走,负数取余是负的:
// 7 % 2 // 1 -7 % 2 // -1 ← 不是 1!符号跟着 -7
// 所以判奇偶:
// n % 2 === 0 // ✅ 判偶数,对正负数都成立
// n % 2 === 1 // ❌ 判奇数,遇负数会漏:-7%2 是 -1,=== 1 得 false → 误判成偶数
// 结论:判奇偶一律用 n % 2 === 0(判偶),再用 else 处理奇数。别用 === 1。
//
// 2. 赋值运算符
// = 是"赋值"(把右边存进左边),下面是简写:
// ┌────────┬──────────────┬────────┐
// │ 写法 │ 等价于 │ 说明 │
// ├────────┼──────────────┼────────┤
// │ x += 5 │ x = x + 5 │ 加等于 │
// │ x -= 5 │ x = x - 5 │ 减等于 │
// │ x *= 2 │ x = x * 2 │ 乘等于 │
// │ x /= 2 │ x = x / 2 │ 除等于 │
// └────────┴──────────────┴────────┘
//
// 3. 比较运算符 → 结果是布尔值 ⭐
// ┌───────────────┬──────────────────────────────┬──────────┬──────────┐
// │ 符号 │ 含义 │ 例子 │ 结果 │
// ├───────────────┼──────────────────────────────┼──────────┼──────────┤
// │ > < >= <= │ 大小比较 │ 5 > 3 │ true │
// │ === │ 严格等于(值和类型都要相等) │ 1 === 1 │ true │
// │ !== │ 严格不等 │ 1 !== 2 │ true │
// │ == │ 松散等于(会偷偷转类型) │ 1 == '1' │ true ⚠️ │
// └───────────────┴──────────────────────────────┴──────────┴──────────┘
// ⭐⭐ 最重要的一条规则:永远用 ===,别用 ==
// == 会做"隐式类型转换",制造诡异结果:
// 1 == '1' // true ← 数字 1 和字符串 '1' 被判相等?!
// 1 === '1' // false ← === 看类型,数字 ≠ 字符串,正确
// 0 == '' // true ← 更离谱
// 记死这条:比较一律用 === 和 !==。这是 JS 最著名的坑之一。
//
// 4. 逻辑运算符
// ┌──────┬───────────┬──────────────────┐
// │ 符号 │ 含义 │ 口诀 │
// ├──────┼───────────┼──────────────────┤
// │ && │ 与(AND) │ 都为真才真 │
// │ || │ 或(OR) │ 有一个真就真 │
// │ ! │ 非(NOT) │ 真假反转 │
// └──────┴───────────┴──────────────────┘
// true && false // false true || false // true !true // false
// 常用于条件判断:age >= 18 && hasTicket(成年且有票)。
//
// 5. 一个坑:+ 对字符串是"拼接" ⭐
// 1 + 2 // 3 ← 数字相加
// '1' + '2' // '12' ← 字符串拼接
// '1' + 2 // '12' ← 只要有一个是字符串,就拼接!
// 1 + '2' + 3 // '123' ← 从左到右,一旦变字符串后面全拼
// 其他运算符(- * /)不会拼接,反而会把字符串转成数字再算:
// '6' - 2 === 4、'6' * 2 === 12。只有 + 特殊。
//
// 6. 类型转换:显式 vs 隐式 ⭐⭐
// 上面这些坑的根源,都是 JS 在偷偷"转类型"。
// 隐式转换(JS 自作主张,坑的来源)—— 你没让它转,它自己转了:
// '5' + 3 // '53' ← 偷偷把 3 转成字符串,拼接
// '5' - 3 // 2 ← 偷偷把 '5' 转成数字
// 0 == '' // true ← 偷偷都转成数字比较
// 'abc' - 1 // NaN ← 转不成数字 → NaN(会像病毒一样传染后续计算)
// 显式转换(你主动写,推荐 ✅)—— 意图清楚、无意外:
// Number('42') // 42 Number('abc') // NaN ← 转不了就是 NaN
// parseInt('42px')// 42 parseFloat('3.14') // 3.14
// String(42) // '42'
// Boolean('') // false Boolean('hi') // true Boolean(0) // false
// 方向性差异(记住):
// - 数字 → 字符串:几乎总能成功
// - 字符串 → 数字:可能失败,失败得 NaN(处理用户输入时要留心)
// 原则:能用显式(Number()/String())就别靠隐式。
//
// 7. TypeScript 能克服这些坑吗?—— 部分能 ⭐
// TS 在写代码时(编译期)检查类型,能挡一大类,但不是全部:
// ┌──────────────────────────────────────────────┬───────────────┐
// │ 坑 │ TS 能挡吗 │
// ├──────────────────────────────────────────────┼───────────────┤
// │ '5' - 3(类型不匹配的运算) │ ✅ 编译期报错 │
// │ 0 == ''(乱比较) │ ✅ 报错 │
// │ '5' + 3(拼接,语法合法) │ ❌ 不管 │
// │ 外部数据(输入/请求/JSON.parse)类型错 │ ❌ 只信标注 │
// │ 运行时才暴露的问题 │ ❌ 类型已消失 │
// └──────────────────────────────────────────────┴───────────────┘
// 心智模型:TS 是"写代码时的第一道墙",挡掉大部分手滑;但 + 拼接、外部数据
// 这些盲区,仍要靠显式转换 + 校验输入兜底。理解 JS 转换原理,是用好 TS 的前提。
//
// ✅ 本主题要点回顾
// 1. 算术:/ 不取整、% 取余判奇偶、** 次方
// 2. 赋值简写:+= -= *= /=
// 3. 比较永远用 === / !==,别用 == ⭐⭐
// 4. 逻辑:&&(都真)、||(有一真)、!(反转)
// 5. + 遇字符串会拼接,其他运算符会转数字 ⭐
// 6. 能用显式转换(Number()/String())就别靠隐式 ⭐
// 7. TS 能挡"类型不匹配"的坑,但挡不住 + 拼接和外部数据 —— 显式转换的习惯不能丢
// ═════ 示范 ═════
// ── 1. 算术运算符 ─────────────────────────────────
console.log(2 + 3) // 5
console.log(5 - 2) // 3
console.log(4 * 3) // 12
console.log(10 / 4) // 2.5 ← JS 除法不取整!
console.log(10 % 3) // 1 ← 取余
console.log(2 ** 3) // 8 ← 2 的 3 次方
// ── 2. % 取余判断奇偶 ⭐ ──────────────────────────
console.log(10 % 2) // 0 ← 余 0 → 偶数
console.log(7 % 2) // 1 ← 余 1 → 奇数
console.log(10 % 2 === 0) // true ← 判断偶数的标准写法
// ── 3. 赋值简写 ───────────────────────────────────
let x = 10
x += 5 // 等价 x = x + 5
console.log(x) // 15
x *= 2 // 等价 x = x * 2
console.log(x) // 30
// ── 4. 比较运算符 → 布尔值 ────────────────────────
console.log(5 > 3) // true
console.log(5 <= 4) // false
console.log(1 === 1) // true
console.log(1 !== 2) // true
// ── 5. === vs == 的坑 ⭐⭐(最重要)────────────────
console.log(1 === '1') // false ← 严格:类型不同 → 不等(正确做法)
console.log(1 == '1') // true ← 松散:偷偷转类型 → 相等(危险!)
console.log(0 == '') // true ← == 的离谱行为
// 结论:永远用 ===,别用 ==
// ── 6. 逻辑运算符 ─────────────────────────────────
console.log(true && false) // false ← 都真才真
console.log(true || false) // true ← 有一真就真
console.log(!true) // false ← 反转
const age = 20
const hasTicket = true
console.log(age >= 18 && hasTicket) // true ← 成年且有票
// ── 7. + 遇字符串会拼接 ⭐ ────────────────────────
console.log(1 + 2) // 3 ← 数字相加
console.log('1' + '2') // '12' ← 字符串拼接
console.log('1' + 2) // '12' ← 有一个字符串就拼接
console.log(1 + '2' + 3) // '123' ← 从左到右,变字符串后全拼
// 其他运算符反而会把字符串转成数字:
console.log('6' - 2) // 4 ← - 让 '6' 变数字 6
console.log('6' * 2) // 12 ← * 同理
// ═════ 轮到你 ═════
// 👇 轮到你(把 ??? 换掉,取消注释,跑一下对答案)
// 练习 A:判断 15 是不是偶数(用 % 和 ===),应打印 false
// console.log(15 % 2 === 0)
// 练习 B:一个人 age = 16,判断"成年 且 有票",应打印 false
// const myAge = ???
// const ticket = true
// console.log(??? >= 18 && ticket)
// 练习 C:猜一下 '5' + 3 和 '5' - 3 分别是什么?先猜再取消注释验证
// console.log('5' + 3) // 你猜:___
// console.log('5' - 3) // 你猜:___
// 练习 A:判断 15 是不是偶数 → 要用 === 才是"判断"
console.log(15 % 2 === 0) // false ← 15%2 是 1,1===0 为 false
console.log(15 % 2) // 1 ← 对比:这只是算余数,不是判断
// 练习 B:age=16,判断"成年(>=18)且有票"
const myAge = 16
const ticket = true
console.log(myAge >= 18 && ticket) // false ← 16 不够 18,&& 整体为 false
// 练习 C:先猜后验(你猜的 '53' 和 2 都对!)
console.log('5' + 3) // '53' ← + 遇字符串 → 拼接
console.log('5' - 3) // 2 ← - 把 '5' 转成数字 → 5-3