分组()运算符控制表达式中求值的优先级。它还充当某些语法结构中任意表达式的容器,否则会发生歧义或语法错误。
优先级
3 * (2 + 1) // 9
请注意,在上述示例中,运算符计算的顺序已更改,但操作数计算的顺序没有更改。例如,在此代码中,在考虑运算符顺序之前,函数调用a()、b()和c()从左到右计算(正常计算顺序)。
a() * (b() + c());
* + 表示运算符,3 2 1 或 a() b() c() 表示操作数。
() 为所有运算符中最高优先级,在你不明确运算符的优先级时,可以用 () 显式的标明优先级。
使用分组运算符消除解析歧义
表达式语句不能以关键字函数开头,因为解析器会将其视为函数声明的开始。
无论这样写
// 这样写是错误的
function () {
// code
}();
还是
// 这样写是错误的
function(){
// code
}
都是会报错的。因为js的引擎会把这里的 function 看成是函数声明,而函数声明不允许没有函数名,因此会对匿名函数报错。
匿名函数只允许以表达式的形式存在,例如:
setTimeout(function(){
/* 代码 */
}, 1000);
这里的匿名函数就是作为 setTimeout 的一个参数,是表达式,这种写法是允许的。
或者:
const foo = function(){
/* 代码 */
};
把一个匿名函数赋值给一个变量,这是一个const语句,而匿名函数在该语句中充当函数表达式的角色。
如果这里的函数有名字呢?不会报错,但语义会发生变化。例如:
function foo(x){
/* 代码 */
}(1);
控制台里会输出“1”。
原因是js引擎会认为前面的函数是一个函数声明的语句,而后面的(1)是另一个单独的语句,于是执行后面的语句,在控制台输出1。它实际上等价于:
function foo(x){
/* 代码 */
};
(1);
分组运算符可用于消除这种歧义,因为当解析器看到左括号时,它知道后面的内容必须是表达式而不是声明。
(function () {
// code
})();
或者使用一元运算符,void 提供了最好的语义,因为它明确表示函数调用的返回值应该被丢弃。
void function() {}()
()()的理解
(function (a, b) {
// code
})(1, 2);
可以尝试把匿名函数赋值给 foo ,再执行 foo(1,2)
const foo = function(a,b) {}
foo(1, 2)
一道面试题
var a, b = 2
;(function() {
console.log(a)
console.log(b)
a = (b = 5)
var b
console.log(a)
console.log(b)
})()
console.log(a)
答案
undefined
undefined
5
5
5
2
解析:
var a, b = 2 //声明全局变量a、b,并给b赋值为2
;(function() {
console.log(a) // undefined,a为全局变量没有被赋值
console.log(b) // undefined,
// 这里b为局部变量,因为在匿名函数中也声明了变量b并赋值为3,
// 但var存在变量提升,只是声明提升,所以为undefined,并不会报错
var b = 3
a = (b = 5)
console.log(a) // 5
console.log(b) // 5
})()
console.log(a) // 5 a为全局变量,在匿名函数中被赋值为5
console.log(b) // 2 匿名函数中重新声明了局部变量b,匿名函数中对b的操作,不影响全局变量b的值
赋值运算符是右结合的,所以:
a = b = 5; // 相当于 a = (b = 5);
预期结果是 a 和 b 的值都会成为 5。这是因为赋值运算符的返回结果就是赋值运算符右边的那个值,具体过程是:首先 b 被赋值为 5,然后 a 也被赋值为 b = 5 的返回值,也就是 5。