연산자 우선순위
연산자 우선순위는 연산자를 실행하는 순서를 결정합니다. 우선순위가 높은 연산자가 먼저 실행됩니다.
시도해보기
우선순위와 결합성
아래와 같이 표현할 수 있는 표현식을 생각해 봅시다. 연산자1과 연산자2의 자리에는 아무 연산자를 넣을 수 있습니다.
a 연산자1 b 연산자2 c
두 연산자의 우선순위(아래 표 참조)가 다를 경우, 우선순위가 높은 연산자가 먼저 실행되고 결합성은 영향을 미치지 않습니다. 아래 예제에서는 덧셈이 곱셈보다 먼저 쓰였음에도 곱셈의 우선순위가 높기 때문에 먼저 실행됩니다.
console.log(3 + 10 * 2); // 23을 출력
console.log(3 + 10 * 2); // 23을 출력, 괄호는 불필요함
console.log((3 + 10) * 2); // 26을 출력, 괄호로 인해 실행 순서가 바뀜
좌결합성(왼쪽에서 오른쪽으로)은 표현식이 (a 연산자1 b) 연산자2 c
와 같이, 우결합성(오른쪽에서 왼쪽으로)은 a 연산자1 (b 연산자2 c)
와 같이 계산된다는 의미입니다. 대입 연산자는 우결합성이므로 다음과 같은 코드를 작성할 수 있습니다.
a = b = 5; // a = (b = 5);와 같음
이때 대입 연산자는 대입된 값을 반환하므로 a
와 b
의 값이 5가 됨을 예상할 수 있습니다. 우선 b
의 값이 5로 설정되고, 그 다음에는 a
의 값이 우변인 b = 5
의 반환값 5로 설정됩니다.
다른 예시로, 좌결합성인 다른 산술 연산자와 달리 거듭제곱 연산자 (**
)만은 우결합성입니다. 흥미로운 점으로 표현식의 평가는 결합성과 무관하게 항상 왼쪽에서 오른쪽으로 진행됩니다.
코드 | 출력 |
js
|
첫째 항 평가함 둘째 항 평가함 3 |
js
|
첫째 항 평가함 둘째 항 평가함 8 |
여러 연산자의 우선순위가 같을 때는 결합성을 고려합니다. 위에서와 같이 연산자가 하나이거나 연산자끼리 우선순위가 다를 경우에는 결합성이 결과에 영향을 미치지 않습니다. 아래의 예제에서 같은 종류의 연산자를 여러 번 사용했을 때 결합성이 결과에 영향을 미치는 것을 확인할 수 있습니다.
코드 | 출력 |
js
|
첫째 항 평가함 둘째 항 평가함 셋째 항 평가함 1 |
js
|
첫째 항 평가함 둘째 항 평가함 셋째 항 평가함 512 |
js
|
첫째 항 평가함 둘째 항 평가함 셋째 항 평가함 64 |
위의 예제에서 나눗셈은 좌결합성이므로 6 / 3 / 2
는 (6 / 3) / 2
와 같습니다. 한편 거듭제곱은 우결합성이므로 2 ** 3 ** 2
는 2 ** (3 ** 2)
와 같습니다. 그러므로 (2 ** 3) ** 2
는 괄호로 인해 실행 순서가 바뀌기 때문에 위 표와 같이 64로 평가됩니다.
우선순위는 결합성보다 항상 우선하므로, 거듭제곱과 나눗셈을 같이 사용하면 나눗셈보다 거듭제곱이 먼저 계산됩니다. 예를 들어 2 ** 3 / 3 ** 2
는 (2 ** 3) / (3 ** 2)
와 같으므로 0.8888888888888888로 계산됩니다.
예제
3 > 2 && 2 > 1;
// true를 반환
3 > 2 > 1;
// 3 > 2는 true인데, 부등호 연산자에서 true는 1로 변환되므로
// true > 1은 1 > 1이 되고, 이는 거짓이다.
// 괄호를 추가하면 (3 > 2) > 1과 같다.
그룹화와 단락에 대한 참고사항
아래 표에서 그룹화는 우선 순위가 가장 높은 것으로 나열됩니다. 그러나 이것이 항상 그룹화 기호( … )
내의 표현식이, 특히 단락과 관련하여 먼저 평가된다는 것을 의미하지는 않습니다.
단락은 조건부 평가의 전문 용어입니다. 예를 들어 표현식 a &&(b + c)
에서 a
가 falsy이면 하위 표현식(b + c)
은 괄호 안에 있어도 평가되지 않습니다. 논리 분리 연산자("OR")가 "단락"되었다고 말할 수 있습니다. 논리적 분리와 함께 다른 단락 연산자에는 논리적 연결("AND"), nullish-coalescing, 선택적 연결 및 조건부 연산자가 포함됩니다. 몇 가지 예가 더 있습니다.
a || b * c; // `a`를 먼저 평가하고, `a`가 "truthy"라면 `a`를 생성합니다.
a && b < c; // `a`를 먼저 평가하고, `a`가 "falsy"라면 `a`를 생성합니다.
a ?? (b || c); // `a`를 먼저 평가하고, `a`가 `null`과 `undefined`가 아니라면 `a`를 생성합니다.
a?.b.c; // `a`를 먼저 평가하고, `a`가 `null`또는 `undefined`라면 `undefined`를 생성합니다.
표
다음 표는 우선순위 내림차순(19부터 1까지)으로 정렬되어 있습니다.
우선순위 | 연산자 유형 | 결합성 | 연산자 |
---|---|---|---|
19 | 그룹 | 없음 | ( … ) |
18 | 멤버 접근 | 좌결합성 | … . … |
계산된 멤버 접근 | 좌결합성 | … [ … ] |
|
new (인자 리스트 제공)
|
없음 | new … ( … ) |
|
함수 호출 | 좌결합성 |
… ( … )
|
|
옵셔널 체이닝 | 좌결합성 | ?. |
|
17 |
new (인자 리스트 생략)
|
우결합성 | new … |
16 | 후위 증가 | 없음 | … ++ |
후위 감소 | … -- |
||
15 | 논리 NOT | 우결합성 | ! … |
비트 NOT | ~ … |
||
단항 양부호 | + … |
||
단항 부정 | - … |
||
전위 증가 | ++ … |
||
전위 감소 | -- … |
||
typeof |
typeof … |
||
void |
void … |
||
delete |
delete … |
||
await |
await … |
||
14 | 거듭제곱 | 우결합성 | … ** … |
13 | 곱하기 | 좌결합성 | … * … |
나누기 | … / … |
||
나머지 | … % … |
||
12 | 더하기 | 좌결합성 | … + … |
빼기 | … - … |
||
11 | 비트 왼쪽 시프트 | 좌결합성 | … << … |
비트 오른쪽 시프트 | … >> … |
||
비트 부호 없는 오른쪽 시프트 | … >>> … |
||
10 | 미만 | 좌결합성 | … < … |
이하 | … <= … |
||
초과 | … > … |
||
이상 | … >= … |
||
in |
… in … |
||
instanceof |
… instanceof … |
||
9 | 동등 | 좌결합성 | … == … |
부등 | … != … |
||
일치 | … === … |
||
불일치 | … !== … |
||
7 | 비트 AND | 좌결합성 | … & … |
7 | 비트 XOR | 좌결합성 | … ^ … |
6 | 비트 OR | 좌결합성 | … | … |
5 | 논리 AND | 좌결합성 | … && … |
4 | 논리 OR | 좌결합성 | … || … |
널 병합 연산자 | 좌결합성 | … ?? … |
|
3 | 조건 (삼항) | 우결합성 | … ? … : … |
2 | 할당 | 우결합성 | … = … |
… += … |
|||
… -= … |
|||
… **= … |
|||
… *= … |
|||
… /= … |
|||
… %= … |
|||
… <<= … |
|||
… >>= … |
|||
… >>>= … |
|||
… &= … |
|||
… ^= … |
|||
… |= … |
|||
… &&= … |
|||
… ||= … |
|||
… ??= … |
|||
yield |
우결합성 | yield … |
|
yield* |
yield* … |
||
1 | 쉼표 / 시퀀스 | 좌결합성 | … , … |