JavaScript表达式的优先级规则
2024-08-193.0k 阅读
运算符优先级概述
在JavaScript编程中,当一个表达式包含多个运算符时,运算符优先级规则决定了它们的执行顺序。这类似于数学中的运算优先级,例如先乘除后加减。不同的运算符优先级不同,正确理解和运用这些优先级对于编写准确无误的JavaScript代码至关重要。如果不遵循正确的优先级,代码可能会产生意想不到的结果。
优先级顺序详细介绍
- 括号 ()
- 括号在JavaScript表达式优先级中具有最高的优先级。使用括号可以强制改变表达式的计算顺序。例如:
let a = 2 + 3 * 4;
let b = (2 + 3) * 4;
console.log(a); // 输出14,先计算乘法3 * 4 = 12,再加上2
console.log(b); // 输出20,先计算括号内2 + 3 = 5,再乘以4
- 当表达式中有多层括号时,从最内层的括号开始计算。例如:
let c = ((2 + 3) * (4 - 1)) / 3;
console.log(c); // 先计算最内层括号2 + 3 = 5和4 - 1 = 3,然后5 * 3 = 15,最后15 / 3 = 5
- 成员访问 . 、数组下标 [] 、函数调用 ()
- 成员访问运算符 .:用于访问对象的属性。例如:
let person = {name: 'John', age: 30};
console.log(person.name); // 输出'John',这里.运算符优先级较高,先确定要访问person对象的name属性
- 数组下标 []:用于访问数组中的元素。例如:
let numbers = [1, 2, 3];
console.log(numbers[1]); // 输出2,[]运算符优先级高,先确定访问numbers数组下标为1的元素
- 函数调用 ():用于调用函数。例如:
function add(a, b) {
return a + b;
}
let result = add(2, 3);
console.log(result); // 输出5,()运算符用于调用add函数,其优先级较高
- 当这些运算符同时出现在一个表达式中时,按照从左到右的顺序进行计算(因为它们优先级相同)。例如:
let obj = {
arr: [1, 2, 3],
getValue: function() {
return this.arr[2];
}
};
console.log(obj.getValue()); // 先通过.访问obj的getValue函数,然后调用该函数,在函数内通过[]访问arr数组下标为2的元素,输出3
- new 无参数列表
new
关键字用于创建对象实例。当new
后没有参数列表时,它的优先级较高。例如:
function MyConstructor() {
this.value = 10;
}
let myObj = new MyConstructor;
console.log(myObj.value); // 输出10,new MyConstructor先执行,创建MyConstructor的实例
- new 有参数列表
- 当
new
关键字带有参数列表时,其优先级与无参数列表时相同,但在表达式中的执行逻辑会因参数而有所不同。例如:
- 当
function Person(name, age) {
this.name = name;
this.age = age;
}
let person1 = new Person('Alice', 25);
console.log(person1.name); // 输出'Alice',new Person('Alice', 25)先执行,创建Person的实例并传入参数
- 后置递增 ++ 、后置递减 --
- 后置递增和后置递减运算符在使用后才改变变量的值。例如:
let num1 = 5;
let result1 = num1++;
console.log(result1); // 输出5,先返回num1的值5,然后num1自增为6
console.log(num1); // 输出6
let num2 = 3;
let result2 = num2--;
console.log(result2); // 输出3,先返回num2的值3,然后num2自减为2
console.log(num2); // 输出2
- 当后置递增/递减与其他运算符混合使用时,先按照其他运算符的优先级计算表达式,然后再进行后置递增/递减操作。例如:
let x = 2;
let y = 3 + x++;
console.log(y); // 输出5,先计算3 + x,此时x为2,结果为5,然后x自增为3
console.log(x); // 输出3
- 逻辑非 ! 、按位非 ~ 、一元加法 + 、一元减法 - 、typeof 、void 、delete
- 逻辑非 !:用于对布尔值取反。例如:
let bool1 = true;
console.log(!bool1); // 输出false
- 按位非 ~:对数字的二进制表示进行按位取反。例如:
let num3 = 5; // 二进制为00000101
let result3 = ~num3; // 二进制按位取反为11111010,转换为十进制为 -6
console.log(result3); // 输出 -6
- 一元加法 +:可以将非数字类型转换为数字类型。例如:
let str = '10';
let num4 = +str;
console.log(num4); // 输出10,将字符串'10'转换为数字10
- 一元减法 -:可以对数字取负。例如:
let num5 = 10;
let num6 = -num5;
console.log(num6); // 输出 -10
- typeof:用于返回变量的数据类型。例如:
let var1 = 'hello';
console.log(typeof var1); // 输出'string'
- void:用于计算一个表达式的值,但返回
undefined
。例如:
let result4 = void(1 + 2);
console.log(result4); // 输出undefined,虽然1 + 2计算结果为3,但void使其返回undefined
- delete:用于删除对象的属性。例如:
let obj1 = {prop: 'value'};
delete obj1.prop;
console.log(obj1.prop); // 输出undefined,属性prop被删除
- 这些运算符优先级相同,在表达式中按照从左到右的顺序进行计算。例如:
let val1 = -~!5;
// 先计算!5,结果为false
// 再计算~false,false转换为0,~0为 -1
// 最后计算 -(-1),结果为1
console.log(val1); // 输出1
- 前置递增 ++ 、前置递减 --
- 前置递增和前置递减运算符在使用前就改变变量的值。例如:
let num7 = 5;
let result5 = ++num7;
console.log(result5); // 输出6,num7先自增为6,然后返回6
console.log(num7); // 输出6
let num8 = 3;
let result6 = --num8;
console.log(result6); // 输出2,num8先自减为2,然后返回2
console.log(num8); // 输出2
- 当与其他运算符混合使用时,先进行前置递增/递减操作,再按照其他运算符的优先级计算表达式。例如:
let m = 2;
let n = ++m + 3;
// 先执行++m,m变为3
// 再计算3 + 3,结果为6
console.log(n); // 输出6
console.log(m); // 输出3
- 乘法 * 、除法 / 、取模 %
- **乘法 ***:用于两个数相乘。例如:
let a1 = 2;
let b1 = 3;
let product = a1 * b1;
console.log(product); // 输出6
- 除法 /:用于两个数相除。例如:
let a2 = 10;
let b2 = 2;
let quotient = a2 / b2;
console.log(quotient); // 输出5
- 取模 %:用于获取两个数相除的余数。例如:
let a3 = 7;
let b3 = 3;
let remainder = a3 % b3;
console.log(remainder); // 输出1
- 这些运算符优先级相同,在表达式中按照从左到右的顺序进行计算。例如:
let result7 = 10 / 2 * 3 % 2;
// 先计算10 / 2 = 5
// 再计算5 * 3 = 15
// 最后计算15 % 2 = 1
console.log(result7); // 输出1
- 加法 + 、减法 -
- 加法 +:可以用于数字相加或字符串拼接,取决于操作数的类型。例如:
let num9 = 2;
let num10 = 3;
let sum1 = num9 + num10;
console.log(sum1); // 输出5,数字相加
let str1 = 'hello';
let str2 = ' world';
let strSum = str1 + str2;
console.log(strSum); // 输出'hello world',字符串拼接
- 减法 -:用于两个数字相减。例如:
let num11 = 5;
let num12 = 2;
let diff = num11 - num12;
console.log(diff); // 输出3
- 当加法和减法与其他优先级较低的运算符混合使用时,先按照其他运算符优先级计算,再进行加法和减法运算。例如:
let result8 = 2 * (3 + 1) - 5;
// 先计算括号内3 + 1 = 4
// 再计算2 * 4 = 8
// 最后计算8 - 5 = 3
console.log(result8); // 输出3
- 位左移 << 、位右移 >> 、无符号右移 >>>
- 位左移 <<:将二进制数向左移动指定的位数,右边空出的位用0填充。例如:
let num13 = 5; // 二进制为00000101
let result9 = num13 << 2; // 左移2位后为00010100,转换为十进制为20
console.log(result9); // 输出20
- **位右移 >>**:将二进制数向右移动指定的位数,左边空出的位用符号位填充(正数用0,负数用1)。例如:
let num14 = 20; // 二进制为00010100
let result10 = num14 >> 2; // 右移2位后为00000101,转换为十进制为5
console.log(result10); // 输出5
- **无符号右移 >>>**:将二进制数向右移动指定的位数,左边空出的位始终用0填充。例如:
let num15 = -5; // 二进制为11111011
let result11 = num15 >>> 2; // 无符号右移2位后为00111110,转换为十进制为62
console.log(result11); // 输出62
- 这些运算符优先级相同,在表达式中按照从左到右的顺序进行计算。例如:
let result12 = 5 << 2 >> 1;
// 先计算5 << 2 = 20
// 再计算20 >> 1 = 10
console.log(result12); // 输出10
- 小于 < 、小于等于 <= 、大于 > 、大于等于 >=
- 这些比较运算符用于比较两个值的大小关系,返回布尔值。例如:
let num16 = 5;
let num17 = 3;
console.log(num16 > num17); // 输出true
console.log(num16 <= num17); // 输出false
- 当与其他优先级较低的运算符混合使用时,先按照其他运算符优先级计算,再进行比较运算。例如:
let result13 = 2 * 3 > 4 + 1;
// 先计算2 * 3 = 6和4 + 1 = 5
// 再比较6 > 5,结果为true
console.log(result13); // 输出true
- 等于 == 、不等于 != 、全等 === 、不全等 !==
- 等于 ==:比较两个值是否相等,会进行类型转换。例如:
let num18 = 5;
let str3 = '5';
console.log(num18 == str3); // 输出true,会将字符串'5'转换为数字5后进行比较
- **不等于 !=**:比较两个值是否不相等,会进行类型转换。例如:
let num19 = 3;
let num20 = 5;
console.log(num19 != num20); // 输出true
- **全等 ===**:比较两个值是否相等且类型相同,不会进行类型转换。例如:
let num21 = 5;
let str4 = '5';
console.log(num21 === str4); // 输出false,类型不同
- **不全等 !==**:比较两个值是否不相等或类型不同,不会进行类型转换。例如:
let num22 = 3;
let str5 = '3';
console.log(num22!== str5); // 输出true
- 这些运算符优先级相同,在表达式中按照从左到右的顺序进行计算。例如:
let result14 = 5 == '5' === true;
// 先计算5 == '5',结果为true
// 再计算true === true,结果为true
console.log(result14); // 输出true
- 位与 &
- 位与运算符对两个二进制数的每一位进行与操作(都为1时结果为1,否则为0)。例如:
let num23 = 5; // 二进制为00000101
let num24 = 3; // 二进制为00000011
let result15 = num23 & num24; // 二进制与操作后为00000001,转换为十进制为1
console.log(result15); // 输出1
- 当与其他优先级较低的运算符混合使用时,先按照其他运算符优先级计算,再进行位与运算。例如:
let result16 = (5 + 3) & 2;
// 先计算5 + 3 = 8,二进制为00001000
// 再与2(二进制00000010)进行位与操作,结果为0,二进制为00000000
console.log(result16); // 输出0
- 位异或 ^
- 位异或运算符对两个二进制数的每一位进行异或操作(不同为1,相同为0)。例如:
let num25 = 5; // 二进制为00000101
let num26 = 3; // 二进制为00000011
let result17 = num25 ^ num26; // 二进制异或操作后为00000110,转换为十进制为6
console.log(result17); // 输出6
- 与其他优先级较低的运算符混合使用时,遵循先高后低的优先级顺序。例如:
let result18 = (4 * 2) ^ 3;
// 先计算4 * 2 = 8,二进制为00001000
// 再与3(二进制00000011)进行位异或操作,结果为11,二进制为00001011
console.log(result18); // 输出11
- 位或 |
- 位或运算符对两个二进制数的每一位进行或操作(只要有一个为1结果就为1)。例如:
let num27 = 5; // 二进制为00000101
let num28 = 3; // 二进制为00000011
let result19 = num27 | num28; // 二进制或操作后为00000111,转换为十进制为7
console.log(result19); // 输出7
- 同样,在与其他运算符混合时按优先级顺序执行。例如:
let result20 = (6 - 2) | 1;
// 先计算6 - 2 = 4,二进制为00000100
// 再与1(二进制00000001)进行位或操作,结果为5,二进制为00000101
console.log(result20); // 输出5
- 逻辑与 &&
- 逻辑与运算符用于连接两个布尔表达式,只有当两个表达式都为
true
时,结果才为true
。例如:
- 逻辑与运算符用于连接两个布尔表达式,只有当两个表达式都为
let bool2 = true;
let bool3 = false;
console.log(bool2 && bool3); // 输出false
- 逻辑与具有短路特性,即如果第一个表达式为 `false`,则不会计算第二个表达式。例如:
function check() {
console.log('This function is not called');
return true;
}
let result21 = false && check();
// 因为第一个表达式为false,check函数不会被调用
- 在与其他优先级较低的运算符混合使用时,先按高优先级运算符计算,再进行逻辑与运算。例如:
let result22 = (2 > 1) && (3 < 5);
// 先计算2 > 1为true,3 < 5为true
// 再进行逻辑与操作,结果为true
console.log(result22); // 输出true
- 逻辑或 ||
- 逻辑或运算符用于连接两个布尔表达式,只要有一个表达式为
true
,结果就为true
。例如:
- 逻辑或运算符用于连接两个布尔表达式,只要有一个表达式为
let bool4 = true;
let bool5 = false;
console.log(bool4 || bool5); // 输出true
- 逻辑或也有短路特性,即如果第一个表达式为 `true`,则不会计算第二个表达式。例如:
function anotherCheck() {
console.log('This function is not called');
return false;
}
let result23 = true || anotherCheck();
// 因为第一个表达式为true,anotherCheck函数不会被调用
- 与其他运算符混合时按优先级顺序运算。例如:
let result24 = (2 < 1) || (3 > 5);
// 先计算2 < 1为false,3 > 5为false
// 再进行逻辑或操作,结果为false
console.log(result24); // 输出false
- 条件运算符 ? :
- 条件运算符是JavaScript中唯一的三元运算符,语法为
condition? expression1 : expression2
。如果condition
为true
,则返回expression1
的值,否则返回expression2
的值。例如:
- 条件运算符是JavaScript中唯一的三元运算符,语法为
let num29 = 5;
let result25 = num29 > 3? 'Greater' : 'Less';
console.log(result25); // 输出'Greater'
- 当与其他运算符混合使用时,根据其优先级进行计算。例如:
let result26 = (2 > 1)? 5 + 3 : 4 - 2;
// 先判断2 > 1为true,然后计算5 + 3,结果为8
console.log(result26); // 输出8
- 赋值运算符 = 、+= 、-= 、*= 、/= 、%= 、<<= 、>>= 、>>>= 、&= 、^= 、|=
- 赋值运算符 =:用于将右边的值赋给左边的变量。例如:
let num30 = 5;
- **复合赋值运算符**:例如 `+=`,`a += b` 等价于 `a = a + b`。其他复合赋值运算符类似。例如:
let num31 = 3;
num31 += 2;
console.log(num31); // 输出5,等价于num31 = num31 + 2
- 赋值运算符优先级较低,通常在其他表达式计算完成后执行。例如:
let x1 = 2;
let y1 = 3;
let z1 = x1 + y1 *= 2;
// 先计算y1 *= 2,y1变为6
// 再计算x1 + y1,结果为8
// 最后将8赋给z1
console.log(z1); // 输出8
- 逗号运算符 ,
- 逗号运算符用于连接多个表达式,并返回最后一个表达式的值。例如:
let result27 = (1 + 2, 3 + 4);
console.log(result27); // 输出7,先计算1 + 2,再计算3 + 4,返回3 + 4的结果
- 在复杂表达式中,逗号运算符优先级最低,最后执行。例如:
let a4 = 2;
let b4 = 3;
let c4 = (a4 += 1, b4 *= 2, a4 + b4);
// 先执行a4 += 1,a4变为3
// 再执行b4 *= 2,b4变为6
// 最后执行a4 + b4,结果为9并赋给c4
console.log(c4); // 输出9
通过深入理解JavaScript表达式的优先级规则,并结合实际的代码示例进行练习,开发者能够编写出更准确、高效的JavaScript代码,避免因优先级问题导致的逻辑错误。在实际编程中,合理使用括号可以使表达式的逻辑更加清晰,即使在复杂的表达式中也能明确计算顺序。