1. JavaScript运算符概述
JavaScript运算符是编程语言中最基础也最重要的组成部分之一。它们允许我们对变量和值执行各种操作,从简单的数学计算到复杂的逻辑判断。理解运算符的工作原理对于编写高效、可读性强的代码至关重要。
在JavaScript中,运算符可以大致分为以下几类:
- 算术运算符:用于执行数学计算
- 赋值运算符:用于给变量赋值
- 比较运算符:用于比较值
- 逻辑运算符:用于组合布尔值
- 位运算符:用于操作二进制位
- 条件(三元)运算符:用于条件判断
- 其他特殊运算符
2. 算术运算符详解
2.1 基本算术运算符
JavaScript提供了标准的算术运算符,包括:
+加法-减法*乘法/除法%取模(求余数)**指数运算(ES2016新增)
javascript复制console.log(10 + 5); // 15
console.log(10 - 5); // 5
console.log(10 * 5); // 50
console.log(10 / 5); // 2
console.log(10 % 3); // 1 (10除以3的余数)
console.log(2 ** 3); // 8 (2的3次方)
2.2 一元算术运算符
一元运算符只需要一个操作数:
++递增--递减+正号(将操作数转换为数字)-负号(取反)
javascript复制let x = 5;
console.log(x++); // 5 (先返回x的值,再递增)
console.log(x); // 6
let y = 5;
console.log(++y); // 6 (先递增,再返回y的值)
console.log(y); // 6
console.log(+'10'); // 10 (将字符串转换为数字)
console.log(-'10'); // -10
3. 赋值运算符详解
3.1 基本赋值运算符
最基本的赋值运算符是=,它将右侧的值赋给左侧的变量。
javascript复制let x = 10;
let y = x; // y现在也是10
3.2 复合赋值运算符
JavaScript提供了多种复合赋值运算符,它们将运算和赋值结合在一起:
javascript复制let x = 10;
x += 5; // 等同于 x = x + 5 → 15
x -= 3; // 等同于 x = x - 3 → 12
x *= 2; // 等同于 x = x * 2 → 24
x /= 4; // 等同于 x = x / 4 → 6
x %= 5; // 等同于 x = x % 5 → 1
x **= 3; // 等同于 x = x ** 3 → 1
3.3 解构赋值
ES6引入了更强大的解构赋值语法:
javascript复制// 数组解构
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 对象解构
const {name, age} = {name: 'Alice', age: 25};
console.log(name, age); // Alice 25
4. 比较运算符详解
4.1 基本比较运算符
JavaScript提供了多种比较运算符:
==等于(会进行类型转换)===严格等于(不进行类型转换)!=不等于!==严格不等于>大于<小于>=大于等于<=小于等于
javascript复制console.log(5 == '5'); // true (类型转换后相等)
console.log(5 === '5'); // false (类型不同)
console.log(5 != '5'); // false
console.log(5 !== '5'); // true
console.log(5 > 3); // true
console.log(5 < 3); // false
4.2 特殊比较情况
javascript复制console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(NaN == NaN); // false (NaN不等于任何值,包括它自己)
console.log(isNaN(NaN)); // true (使用isNaN函数检查NaN)
5. 逻辑运算符详解
5.1 基本逻辑运算符
JavaScript有三个主要的逻辑运算符:
&&逻辑与||逻辑或!逻辑非
javascript复制console.log(true && true); // true
console.log(true && false); // false
console.log(false || true); // true
console.log(!true); // false
5.2 短路求值
逻辑运算符的一个重要特性是短路求值:
javascript复制// && 如果第一个操作数为假,直接返回它,否则返回第二个操作数
console.log(0 && 'Hello'); // 0
console.log(1 && 'Hello'); // 'Hello'
// || 如果第一个操作数为真,直接返回它,否则返回第二个操作数
console.log(0 || 'Hello'); // 'Hello'
console.log(1 || 'Hello'); // 1
5.3 逻辑赋值运算符(ES2021新增)
javascript复制let x = 0;
let y = 5;
x &&= 10; // 等同于 x = x && 10 → x保持0
y &&= 10; // 等同于 y = y && 10 → y变为10
let a = null;
let b = 'Hello';
a ||= 'Default'; // a变为'Default'
b ||= 'Default'; // b保持'Hello'
let c = undefined;
let d = 0;
c ??= 'Default'; // c变为'Default' (仅当c为null或undefined时赋值)
d ??= 'Default'; // d保持0
6. 位运算符详解
6.1 基本位运算符
位运算符直接操作数值的二进制表示:
&按位与|按位或^按位异或~按位非<<左移>>有符号右移>>>无符号右移
javascript复制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 (~00000000000000000000000000000101 = 11111111111111111111111111111010)
console.log(5 << 1); // 10 (0101 << 1 = 1010)
console.log(5 >> 1); // 2 (0101 >> 1 = 0010)
console.log(-5 >>> 1); // 2147483645 (算术右移,高位补0)
6.2 位运算的实用技巧
javascript复制// 快速判断奇偶
function isEven(n) {
return (n & 1) === 0;
}
// 交换两个变量的值(不使用临时变量)
let a = 5, b = 10;
a ^= b;
b ^= a;
a ^= b;
// 快速计算2的n次方
function powerOfTwo(n) {
return 1 << n;
}
// 取整
console.log(~~12.345); // 12
console.log(12.345 | 0); // 12
7. 条件(三元)运算符
条件运算符是JavaScript中唯一的三元运算符,语法为:
condition ? exprIfTrue : exprIfFalse
javascript复制let age = 20;
let status = age >= 18 ? 'Adult' : 'Minor';
console.log(status); // 'Adult'
// 可以嵌套使用,但不宜过度嵌套
let message = age < 13 ? 'Child' :
age < 18 ? 'Teenager' :
age < 65 ? 'Adult' : 'Senior';
8. 其他特殊运算符
8.1 typeof运算符
typeof 返回一个表示操作数类型的字符串:
javascript复制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" (历史遗留问题)
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function(){});// "function"
8.2 instanceof运算符
instanceof 检查对象是否是特定类的实例:
javascript复制console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log('hello' instanceof String);// false (原始类型不是对象)
class Person {}
const john = new Person();
console.log(john instanceof Person); // true
8.3 in运算符
in 检查对象是否包含特定属性:
javascript复制const car = { make: 'Toyota', model: 'Camry' };
console.log('make' in car); // true
console.log('price' in car); // false
const colors = ['red', 'green', 'blue'];
console.log(0 in colors); // true
console.log(3 in colors); // false
console.log('length' in colors);// true
8.4 展开运算符(...)
ES6引入的展开运算符可以用于数组和对象:
javascript复制// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
// 对象展开
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
// 函数参数展开
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
9. 运算符优先级
JavaScript运算符有不同的优先级,决定了它们在表达式中的计算顺序。以下是一些常见的优先级(从高到低):
()分组++ --递增/递减(后缀)! ~ + -逻辑非、按位非、一元加减**指数* / %乘、除、取模+ -加、减<< >> >>>位移< <= > >= in instanceof关系== != === !==相等&按位与^按位异或|按位或&&逻辑与||逻辑或?:条件(三元)= += -=赋值,逗号
javascript复制// 优先级示例
console.log(3 + 4 * 5); // 23 (乘法优先)
console.log((3 + 4) * 5); // 35 (括号改变优先级)
// 复杂的优先级示例
let a = 1, b = 2, c = 3;
console.log(a + b * c ** 2); // 19 (指数>乘法>加法)
10. 运算符使用技巧与注意事项
10.1 类型转换陷阱
javascript复制// 使用==时的隐式类型转换
console.log('' == 0); // true
console.log('0' == 0); // true
console.log(false == 0); // true
// 推荐使用===避免意外
console.log('' === 0); // false
console.log('0' === 0); // false
console.log(false === 0); // false
10.2 短路求值的实用模式
javascript复制// 设置默认值(ES6之前)
function greet(name) {
name = name || 'Guest';
console.log('Hello, ' + name);
}
// ES6默认参数是更好的选择
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
// 条件执行
isLoggedIn && showDashboard();
isAdmin || showAccessDenied();
10.3 浮点数精度问题
javascript复制// JavaScript使用IEEE 754双精度浮点数
console.log(0.1 + 0.2); // 0.30000000000000004
// 解决方案:使用整数运算或四舍五入
console.log((0.1 * 10 + 0.2 * 10) / 10); // 0.3
console.log(Math.round((0.1 + 0.2) * 100) / 100); // 0.3
10.4 性能考虑
javascript复制// 位运算通常比数学运算快
// 但现代JavaScript引擎已经高度优化,差异通常可以忽略
// 缓存计算结果
// 不好的做法:每次循环都计算数组长度
for (let i = 0; i < arr.length; i++) { ... }
// 好的做法:缓存长度
for (let i = 0, len = arr.length; i < len; i++) { ... }
// 现代JavaScript引擎已经优化了这种情况,差异不大
11. ES6+新增运算符特性
11.1 可选链运算符(?.)
ES2020引入的可选链运算符可以安全地访问嵌套对象属性:
javascript复制const user = {
profile: {
name: 'Alice',
address: {
city: 'New York'
}
}
};
// 传统方式
const city = user && user.profile && user.profile.address && user.profile.address.city;
// 使用可选链
const city = user?.profile?.address?.city; // 'New York'
const zip = user?.profile?.address?.zip; // undefined (不会抛出错误)
// 也可用于函数调用
const result = someObject.someMethod?.();
11.2 空值合并运算符(??)
ES2020引入的空值合并运算符提供了一种处理null或undefined的简洁方式:
javascript复制// 传统方式
const value = someValue !== null && someValue !== undefined ? someValue : defaultValue;
// 使用空值合并
const value = someValue ?? defaultValue;
// 与||的区别
console.log(0 || 'default'); // 'default' (0是falsy)
console.log(0 ?? 'default'); // 0 (只有null/undefined会触发)
console.log('' ?? 'default'); // '' (空字符串不是null/undefined)
11.3 指数运算符(**)
ES2016引入的指数运算符是Math.pow()的简写:
javascript复制console.log(2 ** 3); // 8
console.log(2 ** 0.5); // 1.4142135623730951
console.log(2 ** -2); // 0.25
// 右结合性
console.log(2 ** 3 ** 2); // 512 (相当于2 ** (3 ** 2))
12. 运算符在实际开发中的应用
12.1 表单验证
javascript复制function validateForm(data) {
const { username, password, age } = data;
// 使用逻辑运算符组合多个条件
if (!username || !password || age < 18) {
return false;
}
// 更复杂的条件
const isValid = username.length >= 5 &&
/[A-Z]/.test(password) &&
/[0-9]/.test(password) &&
age >= 18 && age <= 120;
return isValid;
}
12.2 权限控制
javascript复制const user = {
isAdmin: true,
permissions: ['read', 'write', 'delete']
};
// 检查权限
function canDelete(user) {
return user.isAdmin || user.permissions.includes('delete');
}
// 使用位掩码进行高级权限控制
const PERMISSIONS = {
READ: 1 << 0, // 1
WRITE: 1 << 1, // 2
DELETE: 1 << 2, // 4
ADMIN: 1 << 3 // 8
};
let userPermissions = PERMISSIONS.READ | PERMISSIONS.WRITE;
function hasPermission(userPermissions, requiredPermission) {
return (userPermissions & requiredPermission) === requiredPermission;
}
console.log(hasPermission(userPermissions, PERMISSIONS.WRITE)); // true
console.log(hasPermission(userPermissions, PERMISSIONS.DELETE)); // false
12.3 性能优化技巧
javascript复制// 使用位运算进行快速整数转换
console.log(12.34 | 0); // 12
console.log(-12.34 | 0); // -12
// 使用~~替代Math.floor (仅适用于32位整数)
console.log(~~12.34); // 12
console.log(~~-12.34); // -12
// 使用异或交换变量
let a = 5, b = 10;
a ^= b; b ^= a; a ^= b;
console.log(a, b); // 10, 5
// 使用位掩码存储多个布尔值
let flags = 0;
const FLAG_A = 1 << 0;
const FLAG_B = 1 << 1;
const FLAG_C = 1 << 2;
// 设置标志
flags |= FLAG_A; // 设置A标志
flags |= FLAG_C; // 设置C标志
// 检查标志
if (flags & FLAG_A) console.log('Flag A is set');
if (flags & FLAG_B) console.log('Flag B is set'); // 不会执行
if (flags & FLAG_C) console.log('Flag C is set');
// 清除标志
flags &= ~FLAG_A; // 清除A标志
