运算符是用于对值进行操���的符号。理解运算符及其优先级,是写出正确表达式的基础。
🎯 算术运算符#
基本运算#
// 加减乘除console.log(10 + 3) // 13console.log(10 - 3) // 7console.log(10 * 3) // 30console.log(10 / 3) // 3.3333...
// 取余(模运算)console.log(10 % 3) // 1console.log(-10 % 3) // -1(符号跟被除数)
// 指数运算(ES2016)console.log(2 ** 3) // 8console.log(4 ** 0.5) // 2(开平方)自增自减#
let a = 5
// 后置:先返回原值,再自增console.log(a++) // 5console.log(a) // 6
// 前置:先自增,再返回新值console.log(++a) // 7console.log(a) // 7
// 自减同理let b = 5console.log(b--) // 5console.log(--b) // 3🔶 避免在复杂表达式中使用自增自减,容易造成困惑:
// 不推荐let x = 1const result = x++ + ++x + x++ // 难以理解
// 推荐:拆分成多个语句let y = 1y += 1const step1 = yy += 1const step2 = y// ...加号的特殊性#
加号既是算术运算符,也是字符串连接符:
// 数字相加console.log(5 + 3) // 8
// 字符串连接console.log('Hello' + ' ' + 'World') // "Hello World"
// 🔶 混合运算��陷阱!)console.log('5' + 3) // "53"(字符串)console.log(5 + '3') // "53"(字符串)console.log(5 + 3 + '2') // "82"(先算 5+3=8,再连接)console.log('2' + 5 + 3) // "253"(从左到右都是连接)
// 强制数值运算console.log(+'5' + 3) // 8console.log(Number('5') + 3) // 8比较运算符#
相等比较#
// == 宽松相等(会类型转换)console.log(5 == '5') // trueconsole.log(0 == false) // trueconsole.log(null == undefined) // trueconsole.log('' == false) // true
// === 严格相等(不转换类型)console.log(5 === '5') // falseconsole.log(0 === false) // falseconsole.log(null === undefined) // false✅ 最佳实践:始终使用 === 和 !==,避免隐式类型转换带来的意外。
大小比较#
// 数值比较console.log(5 > 3) // trueconsole.log(5 >= 5) // trueconsole.log(3 < 5) // trueconsole.log(3 <= 3) // true
// 字符串比较(按 Unicode 码点)console.log('a' < 'b') // trueconsole.log('abc' < 'abd') // trueconsole.log('10' < '9') // true!('1' < '9')
// 混合比较(字符串转��值)console.log('10' > 9) // true(10 > 9)console.log('10' > '9') // false(字符串比较)特殊值比较#
// NaN 与任何值比较都是 falseconsole.log(NaN == NaN) // falseconsole.log(NaN === NaN) // falseconsole.log(NaN > 0) // falseconsole.log(NaN < 0) // false
// null 和 undefinedconsole.log(null == undefined) // trueconsole.log(null === undefined) // falseconsole.log(null > 0) // falseconsole.log(null < 0) // falseconsole.log(null >= 0) // true!(null 转为 0)逻辑运算符#
与或非#
// && 逻辑与console.log(true && true) // trueconsole.log(true && false) // falseconsole.log(false && true) // false
// || 逻辑或console.log(true || false) // trueconsole.log(false || true) // trueconsole.log(false || false) // false
// ! 逻辑非console.log(!true) // falseconsole.log(!false) // trueconsole.log(!0) // trueconsole.log(!'') // trueconsole.log(!null) // true短路求值#
逻辑运算符不一定返回布尔值,而是返回决定结果的那个值:
// && 返回第一个假值,或最后一个值console.log('hello' && 'world') // "world"console.log('' && 'world') // ""console.log(null && 'world') // nullconsole.log(1 && 2 && 3) // 3
// || 返回第一个真值,或最后一个值console.log('' || 'default') // "default"console.log('hello' || 'default') // "hello"console.log(0 || null || 'last') // "last"console.log(0 || '' || false) // false实际应用#
// 默认值function greet(name) { name = name || '游客' return `你好,${name}`}
// 条件执行const user = { name: '张三' }user && console.log(user.name) // "张三"
// 短路保护const config = nullconst port = config && config.port // undefined(不会报错)
// 链式取值(旧写法)const street = user && user.address && user.address.street空值合并运算符 ??#
ES2020 引入的 ?? 只在值为 null 或 undefined 时才取默认值:
// || 的问题:0 和空字符串会被误判console.log(0 || 100) // 100(可能不是预期)console.log('' || 'default') // "default"
// ?? 只对 null/undefined 生效console.log(0 ?? 100) // 0console.log('' ?? 'default') // ""console.log(null ?? 'default') // "default"console.log(undefined ?? 'default') // "default"
// 实际场景function setVolume(level) { level = level ?? 50 // 允许设置为 0 console.log(`音量:${level}`)}setVolume(0) // "音量:0"setVolume() // "音量:50"位运算符#
位运算符将操作数转为 32 位整数进行运算。
基本位运算#
// & 按位与console.log(5 & 3) // 1// 0101 & 0011 = 0001
// | 按位或console.log(5 | 3) // 7// 0101 | 0011 = 0111
// ^ 按位异或console.log(5 ^ 3) // 6// 0101 ^ 0011 = 0110
// ~ 按位取反console.log(~5) // -6// ~0101 = 1010...(32位补码)
// << 左移console.log(5 << 1) // 10// 0101 << 1 = 1010
// >> 有符号右移console.log(5 >> 1) // 2// 0101 >> 1 = 0010
// >>> 无符号右移console.log(-1 >>> 0) // 4294967295实际应用#
// 快速取整(截断小数)console.log(~~3.7) // 3console.log(3.7 | 0) // 3console.log(3.7 >> 0) // 3
// 快速乘除 2 的幂console.log(5 << 2) // 20(5 * 4)console.log(20 >> 2) // 5(20 / 4)
// 判断奇偶console.log(5 & 1) // 1(奇数)console.log(6 & 1) // 0(偶数)
// 交换两个数(不用临时变量)let a = 5, b = 3a = a ^ b // a = 6b = a ^ b // b = 5a = a ^ b // a = 3console.log(a, b) // 3, 5
// 权限管理const READ = 1 // 0001const WRITE = 2 // 0010const EXECUTE = 4 // 0100
let permission = READ | WRITE // 0011 = 3console.log(permission & READ) // 1(有读权限)console.log(permission & EXECUTE) // 0(无执行权限)赋值运算符#
复合赋值#
let x = 10
x += 5 // x = x + 5 = 15x -= 3 // x = x - 3 = 12x *= 2 // x = x * 2 = 24x /= 4 // x = x / 4 = 6x %= 4 // x = x % 4 = 2x **= 3 // x = x ** 3 = 8
// 位运算赋值x &= 3 // x = x & 3x |= 4 // x = x | 4x ^= 1 // x = x ^ 1x <<= 1 // x = x << 1x >>= 1 // x = x >> 1逻辑赋值(ES2021)#
let a = nulllet b = 'hello'let c = ''
// ||= 如果左侧为假值则赋值a ||= 'default' // a = 'default'b ||= 'default' // b = 'hello'(不变)
// &&= 如果左侧为真值则赋值b &&= 'world' // b = 'world'c &&= 'world' // c = ''(不变)
// ??= 如果左侧为 null/undefined 则赋值let d = 0d ??= 100 // d = 0(不变,因为 0 不是 null/undefined)
let e = nulle ??= 100 // e = 100其他运算符#
条件(三元)运算符#
const age = 18const status = age >= 18 ? '成年' : '未成年'console.log(status) // "成年"
// 可以嵌套(但不推荐太深)const score = 85const grade = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 60 ? 'C' : 'D'console.log(grade) // "B"
// 推荐使用 if 或对象映射替代复杂的三元运算逗号运算符#
// 从左到右求值,返回最后一个值const result = (1, 2, 3)console.log(result) // 3
// 常见于 for 循环for (let i = 0, j = 10; i < j; i++, j--) { console.log(i, j)}
// 在一行中执行多个表达式let x = ((a = 1), (b = 2), a + b)console.log(x) // 3可选链运算符 ?.#
安全地访问嵌套属性:
const user = { name: '张三', address: { city: '北京', },}
// 传统写法(繁琐)const zip1 = user && user.address && user.address.zip
// 可选链(简洁)const zip2 = user?.address?.zip // undefined(不报错)
// 方法调用const result = user.getName?.() // undefined(不报错)
// 数组访问const arr = nullconsole.log(arr?.[0]) // undefined
// 配合空值合并const city = user?.address?.city ?? '未知城市' // "北京"typeof 和 instanceof#
// typeof 检查类型console.log(typeof 42) // "number"console.log(typeof 'hello') // "string"console.log(typeof true) // "boolean"console.log(typeof undefined) // "undefined"console.log(typeof null) // "object"(历史bug)console.log(typeof {}) // "object"console.log(typeof []) // "object"console.log(typeof function () {}) // "function"
// instanceof 检查原型链console.log([] instanceof Array) // trueconsole.log([] instanceof Object) // trueconsole.log({} instanceof Array) // falseconsole.log(function () {} instanceof Function) // truedelete 运算符#
const obj = { name: '张三', age: 25 }
delete obj.ageconsole.log(obj) // { name: "张三" }
// 数组元素删除(留下空位)const arr = [1, 2, 3]delete arr[1]console.log(arr) // [1, empty, 3]console.log(arr.length) // 3(长度不变)
// 推荐用 splice 删除数组元素arr.splice(1, 1) // 从索引1删除1个元素in 运算符#
const obj = { name: '张三' }
console.log('name' in obj) // trueconsole.log('age' in obj) // falseconsole.log('toString' in obj) // true(继承的属性)
// 数组中检查索引const arr = [1, 2, 3]console.log(0 in arr) // trueconsole.log(5 in arr) // falseconsole.log('length' in arr) // true运算符优先级#
优先级从高到低排列(部分常用):
| 优先级 | 运算符 | 描述 |
|---|---|---|
| 19 | () | 分组 |
| 18 | . [] () ?. | 成员访问、调用 |
| 17 | new(带参数) | 创建实例 |
| 16 | ++ --(后置) | 后置自增/减 |
| 15 | ! ~ + - ++ -- | 一元运算符 |
| 14 | ** | 指数 |
| 13 | * / % | 乘除取余 |
| 12 | + - | 加减 |
| 11 | << >> >>> | 位移 |
| 10 | < <= > >= in | 比较 |
| 9 | == != === !== | 相等 |
| 8 | & | 按位与 |
| 7 | ^ | 按位异或 |
| 6 | | | 按位或 |
| 5 | && | 逻辑与 |
| 4 | || ?? | 逻辑或 |
| 3 | ? : | 条件 |
| 2 | = += -= 等 | 赋值 |
| 1 | , | 逗号 |
实际例子#
// && 优先于 ||console.log(true || (false && false)) // trueconsole.log((true || false) && false) // false
// 比较优先于逻辑console.log(1 + 2 > 2 && 3 < 4) // true// 等价于 ((1 + 2) > 2) && (3 < 4)
// 一元运算符优先于二元console.log(!true && false) // falseconsole.log(!(true && false)) // true
// 🔶 不确定时就加括号!const result = a > b ? a : c > d ? c : d// 推荐明确写出const result2 = a > b ? a : c > d ? c : d常见陷阱#
🙋 连续比较不是数学意义#
// 数学上:1 < 2 < 3 是对的console.log(1 < 2 < 3) // true,但原因是...// 1 < 2 = true// true < 3 = 1 < 3 = true
console.log(3 > 2 > 1) // false!// 3 > 2 = true// true > 1 = 1 > 1 = false
// 正确写法console.log(1 < 2 && 2 < 3) // true🙋 + 和 - 的一元用法#
// + 转数值console.log(+'42') // 42console.log(+'hello') // NaNconsole.log(+true) // 1console.log(+new Date()) // 时间戳
// - 转负数值console.log(-'42') // -42
// 常见写法const timestamp = +new Date()const num = +inputValue || 0🙋 ?? 不能和 && || 直接混用#
// 语法错误,需要加括号明确优先级// console.log(a || b ?? c) // SyntaxError
console.log((a || b) ?? c) // OKconsole.log(a || (b ?? c)) // OK总结#
| 类别 | 运算符 | 要点 |
|---|---|---|
| 算术 | + - * / % ** | 注意 + 的字符串连接 |
| 比较 | == === != !== > < | 始终用 === |
| 逻辑 | && || ! | 理解短路求值 |
| 空值 | ?? ?. | 安全处理 null/undefined |
| 位运算 | & | ^ ~ << >> | 用于性能优化和权限管理 |
| 赋值 | = += -= &&= ??= | 逻辑赋值是 ES2021 新增 |
| 其他 | typeof instanceof | 类型和原型链检查 |
核心建议:
- 使用
===严格比较 - 用
??代替||处理默认值 - 用
?.安全访问嵌套属性 - 不确定优先级时加括号