1. 运算符基础概念解析
运算符是编程语言中用于执行特定操作的符号或关键字。它们构成了程序逻辑的基础骨架,就像数学中的加减乘除符号一样,让计算机能够理解并执行我们想要的操作。
1.1 运算符的核心作用
运算符主要完成三类工作:
- 数值计算(如加减乘除)
- 逻辑判断(如比较大小)
- 数据操作(如赋值、访问属性)
在JavaScript中,运算符可以处理各种数据类型,包括数字、字符串、布尔值等。例如:
javascript复制let sum = 10 + 20; // 算术运算符
let isGreater = 5 > 3; // 比较运算符
let message = "Hello" + "World"; // 字符串连接
1.2 运算符的组成要素
每个运算符都包含三个关键特性:
- 操作数数量:一元(一个操作数)、二元(两个操作数)或三元(三个操作数)
- 结合性:决定相同优先级运算符的执行顺序(左结合或右结合)
- 优先级:决定不同运算符的执行先后顺序
特别提示:理解运算符优先级是避免程序逻辑错误的关键。当不确定时,使用括号明确运算顺序是最佳实践。
2. JavaScript运算符全解析
2.1 算术运算符
算术运算符用于基本数学运算:
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
| + | 加法 | 3 + 5 | 8 |
| - | 减法 | 10 - 6 | 4 |
| * | 乘法 | 2 * 4 | 8 |
| / | 除法 | 12 / 3 | 4 |
| % | 取模 | 10 % 3 | 1 |
| ** | 幂运算 | 2 ** 3 | 8 |
| ++ | 自增 | let a=1; a++ | 2 |
| -- | 自减 | let b=3; b-- | 2 |
实际开发中容易踩的坑:
javascript复制let x = "5";
let y = 3;
console.log(x + y); // "53" 字符串拼接
console.log(x - y); // 2 数字运算
2.2 比较运算符
比较运算符用于比较两个值:
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
| == | 等于(值相等) | 5 == "5" | true |
| === | 严格等于(值和类型) | 5 === "5" | false |
| != | 不等于 | 5 != "6" | true |
| !== | 严格不等于 | 5 !== "5" | true |
| > | 大于 | 6 > 3 | true |
| < | 小于 | 2 < 1 | false |
| >= | 大于等于 | 5 >= 5 | true |
| <= | 小于等于 | 4 <= 3 | false |
经验法则:在JavaScript中,总是使用===和!==而不是==和!=,可以避免类型转换带来的意外结果。
2.3 逻辑运算符
逻辑运算符用于布尔运算:
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
| && | 逻辑与 | true && false | false |
| || | 逻辑或 | true || false | true |
| ! | 逻辑非 | !true | false |
短路特性示例:
javascript复制// && 遇到false就停止
false && console.log("不会执行");
// || 遇到true就停止
true || console.log("不会执行");
2.4 赋值运算符
赋值运算符用于给变量赋值:
| 运算符 | 示例 | 等价于 |
|---|---|---|
| = | x = 5 | x = 5 |
| += | x += 2 | x = x + 2 |
| -= | x -= 3 | x = x - 3 |
| *= | x *= 4 | x = x * 4 |
| /= | x /= 2 | x = x / 2 |
| %= | x %= 3 | x = x % 3 |
| **= | x **= 2 | x = x ** 2 |
2.5 位运算符
位运算符直接操作二进制位:
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
| & | 按位与 | 5 & 1 | 1 |
| | | 按位或 | 5 | 1 | 5 |
| ^ | 按位异或 | 5 ^ 1 | 4 |
| ~ | 按位非 | ~5 | -6 |
| << | 左移 | 5 << 1 | 10 |
| >> | 右移 | 5 >> 1 | 2 |
| >>> | 无符号右移 | -5 >>> 1 | 2147483645 |
3. 高级运算符技巧
3.1 三元条件运算符
语法:condition ? exprIfTrue : exprIfFalse
javascript复制let age = 20;
let status = age >= 18 ? '成人' : '未成年';
3.2 空值合并运算符(??)
ES2020新增,当左侧为null或undefined时返回右侧值:
javascript复制let value = null;
let result = value ?? '默认值'; // '默认值'
3.3 可选链运算符(?.)
ES2020新增,安全访问嵌套对象属性:
javascript复制const user = {
profile: {
name: 'John'
}
};
console.log(user?.profile?.name); // 'John'
console.log(user?.address?.street); // undefined 而不是报错
3.4 展开运算符(...)
用于展开数组或对象:
javascript复制// 数组展开
let parts = ['shoulders', 'knees'];
let body = ['head', ...parts, 'toes'];
// 对象展开
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { ...obj1, y: 13 };
4. 运算符优先级详解
运算符优先级决定了表达式中运算的执行顺序。以下是从高到低的主要优先级:
- 成员访问 . []、new(带参数列表)、函数调用()
- new(无参数列表)、后置递增/递减
- 逻辑非!、按位非~、一元加减+ -、前置递增/递减、typeof、void、delete、await
- 幂运算 **
- 乘 *、除 /、取模 %
- 加 +、减 -
- 位移 << >> >>>
- 关系 < <= > >= in instanceof
- 相等 == != === !==
- 按位与 &
- 按位异或 ^
- 按位或 |
- 逻辑与 &&
- 逻辑或 ||
- 三元条件 ?:
- 赋值 = += -= 等
- 逗号 ,
实际应用示例:
javascript复制let result = 5 + 3 * 2; // 11 不是16(乘法优先)
let value = a && b || c; // 先执行&&再执行||
5. 运算符重载与自定义
虽然JavaScript本身不支持运算符重载,但我们可以通过一些技巧模拟:
5.1 valueOf()方法实现
javascript复制class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
valueOf() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
}
}
let v1 = new Vector(3, 4);
let v2 = new Vector(1, 2);
console.log(v1 > v2); // true (比较模长)
5.2 自定义运算方法
javascript复制class Complex {
constructor(real, imag) {
this.real = real;
this.imag = imag;
}
add(other) {
return new Complex(this.real + other.real, this.imag + other.imag);
}
toString() {
return `${this.real}+${this.imag}i`;
}
}
let c1 = new Complex(1, 2);
let c2 = new Complex(3, 4);
console.log(c1.add(c2).toString()); // "4+6i"
6. 常见问题与解决方案
6.1 浮点数精度问题
javascript复制console.log(0.1 + 0.2); // 0.30000000000000004
解决方案:
javascript复制// 方法1:使用toFixed
let sum = (0.1 + 0.2).toFixed(1); // "0.3"
// 方法2:转换为整数运算
let sum = (0.1 * 10 + 0.2 * 10) / 10; // 0.3
6.2 类型转换陷阱
javascript复制console.log("5" - 3); // 2
console.log("5" + 3); // "53"
最佳实践:
javascript复制// 显式类型转换
let num = Number("5");
let str = String(3);
6.3 短路求值应用
利用逻辑运算符的短路特性:
javascript复制// 设置默认值
function greet(name) {
name = name || 'Guest';
console.log(`Hello, ${name}`);
}
// 条件执行
user.isAdmin && showAdminPanel();
6.4 运算符优先级混淆
容易出错的例子:
javascript复制let x = 5;
let y = 10;
let z = ++x + y--; // 6 + 10 = 16
清晰写法:
javascript复制let x = 5;
let y = 10;
++x; // 先自增
let z = x + y; // 6 + 10
y--; // 后自减
7. 性能优化技巧
7.1 位运算替代数学运算
javascript复制// 判断奇偶
if (num & 1) {
// 奇数
} else {
// 偶数
}
// 向下取整
let floor = ~~3.14; // 3
7.2 减少属性访问
javascript复制// 不佳写法
for (let i = 0; i < array.length; i++) {
// ...
}
// 优化写法
for (let i = 0, len = array.length; i < len; i++) {
// ...
}
7.3 使用复合赋值运算符
javascript复制// 不佳写法
count = count + 1;
total = total * discount;
// 优化写法
count += 1;
total *= discount;
8. ES6+新增运算符
8.1 指数运算符(**)
javascript复制console.log(2 ** 3); // 8
console.log(2 ** 3 ** 2); // 512 (右结合)
8.2 对象属性简写
javascript复制const name = 'John';
const age = 30;
// ES5
const person = {
name: name,
age: age
};
// ES6+
const person = { name, age };
8.3 解构赋值
javascript复制// 数组解构
const [first, second] = [1, 2];
// 对象解构
const { name, age } = { name: 'John', age: 30 };
// 函数参数解构
function greet({ name, age }) {
console.log(`Hello ${name}, you are ${age}`);
}
9. 类型检测运算符
9.1 typeof运算符
javascript复制typeof 42; // "number"
typeof "hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (历史遗留问题)
typeof []; // "object"
typeof {}; // "object"
typeof function(){}; // "function"
9.2 instanceof运算符
javascript复制class Person {}
let john = new Person();
console.log(john instanceof Person); // true
console.log([] instanceof Array); // true
9.3 in运算符
检查对象是否有某属性:
javascript复制const car = { make: 'Toyota', model: 'Camry' };
console.log('make' in car); // true
console.log('price' in car); // false
10. 运算符最佳实践
- 始终使用===和!==代替==和!=
- 复杂的表达式使用括号明确优先级
- 避免在单个表达式中混合使用多种运算符
- 自增/自减运算符单独使用,避免在复杂表达式中使用
- 使用模板字符串代替字符串拼接
- 合理使用现代运算符(?? ?. ...等)
- 保持运算符两侧的空格一致,增强可读性
- 为特殊的运算符使用添加注释说明
javascript复制// 良好可读的写法
let total = (price * quantity) - discount;
let isValid = (age >= 18) && (status === 'active');
// 使用模板字符串
let greeting = `Hello, ${name}! You are ${age} years old.`;
