연산자 우선순위

연산자 우선순위는 연산자를 실행하는 순서를 결정합니다. 우선순위가 높은 연산자가 먼저 실행됩니다.

시도해보기

우선순위와 결합성

아래와 같이 표현할 수 있는 표현식을 생각해 봅시다. 연산자1과 연산자2의 자리에는 아무 연산자를 넣을 수 있습니다.

a 연산자1 b 연산자2 c

두 연산자의 우선순위(아래 표 참조)가 다를 경우, 우선순위가 높은 연산자가 먼저 실행되고 결합성은 영향을 미치지 않습니다. 아래 예제에서는 덧셈이 곱셈보다 먼저 쓰였음에도 곱셈의 우선순위가 높기 때문에 먼저 실행됩니다.

js
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)와 같이 계산된다는 의미입니다. 대입 연산자는 우결합성이므로 다음과 같은 코드를 작성할 수 있습니다.

js
a = b = 5; // a = (b = 5);와 같음

이때 대입 연산자는 대입된 값을 반환하므로 ab의 값이 5가 됨을 예상할 수 있습니다. 우선 b의 값이 5로 설정되고, 그 다음에는 a의 값이 우변인 b = 5의 반환값 5로 설정됩니다.

다른 예시로, 좌결합성인 다른 산술 연산자와 달리 거듭제곱 연산자 (**)만은 우결합성입니다. 흥미로운 점으로 표현식의 평가는 결합성과 무관하게 항상 왼쪽에서 오른쪽으로 진행됩니다.

코드 출력
js
function echo(name, num) {
    console.log(name + " 항 평가함");
    return num;
}
// 나눗셈 연산자 (/)에 주목
console.log(echo("첫째", 6) / echo("둘째", 2));
첫째 항 평가함
둘째 항 평가함
3
js
function echo(name, num) {
    console.log(name + " 항 평가함");
    return num;
}
// 거듭제곱 연산자 (**)에 주목
console.log(echo("첫째", 2) ** echo("둘째", 3));
첫째 항 평가함
둘째 항 평가함
8

여러 연산자의 우선순위가 같을 때는 결합성을 고려합니다. 위에서와 같이 연산자가 하나이거나 연산자끼리 우선순위가 다를 경우에는 결합성이 결과에 영향을 미치지 않습니다. 아래의 예제에서 같은 종류의 연산자를 여러 번 사용했을 때 결합성이 결과에 영향을 미치는 것을 확인할 수 있습니다.

코드 출력
js
function echo(name, num) {
    console.log(name + " 항 평가함");
    return num;
}
// 나눗셈 연산자 (/)에 주목
console.log(echo("첫째", 6) / echo("둘째", 2) / echo("셋째", 3));
첫째 항 평가함
둘째 항 평가함
셋째 항 평가함
1
js
function echo(name, num) {
    console.log(name + " 항 평가함");
    return num;
}
// 거듭제곱 연산자 (**)에 주목
console.log(echo("첫째", 2) ** echo("둘째", 3) ** echo("셋째", 2));
첫째 항 평가함
둘째 항 평가함
셋째 항 평가함
512
js
function echo(name, num) {
    console.log(name + " 항 평가함");
    return num;
}
// 첫 번째 거듭제곱 연산자 주변의 괄호에 주목
console.log((echo("첫째", 2) ** echo("둘째", 3)) ** echo("셋째", 2));
첫째 항 평가함
둘째 항 평가함
셋째 항 평가함
64

위의 예제에서 나눗셈은 좌결합성이므로 6 / 3 / 2(6 / 3) / 2와 같습니다. 한편 거듭제곱은 우결합성이므로 2 ** 3 ** 22 ** (3 ** 2)와 같습니다. 그러므로 (2 ** 3) ** 2는 괄호로 인해 실행 순서가 바뀌기 때문에 위 표와 같이 64로 평가됩니다.

우선순위는 결합성보다 항상 우선하므로, 거듭제곱과 나눗셈을 같이 사용하면 나눗셈보다 거듭제곱이 먼저 계산됩니다. 예를 들어 2 ** 3 / 3 ** 2(2 ** 3) / (3 ** 2)와 같으므로 0.8888888888888888로 계산됩니다.

예제

js
3 > 2 && 2 > 1;
// true를 반환

3 > 2 > 1;
// 3 > 2는 true인데, 부등호 연산자에서 true는 1로 변환되므로
// true > 1은 1 > 1이 되고, 이는 거짓이다.
// 괄호를 추가하면 (3 > 2) > 1과 같다.

그룹화와 단락에 대한 참고사항

아래 표에서 그룹화는 우선 순위가 가장 높은 것으로 나열됩니다. 그러나 이것이 항상 그룹화 기호( … ) 내의 표현식이, 특히 단락과 관련하여 먼저 평가된다는 것을 의미하지는 않습니다.

단락은 조건부 평가의 전문 용어입니다. 예를 들어 표현식 a &&(b + c)에서 afalsy이면 하위 표현식(b + c)은 괄호 안에 있어도 평가되지 않습니다. 논리 분리 연산자("OR")가 "단락"되었다고 말할 수 있습니다. 논리적 분리와 함께 다른 단락 연산자에는 논리적 연결("AND"), nullish-coalescing, 선택적 연결 및 조건부 연산자가 포함됩니다. 몇 가지 예가 더 있습니다.

js
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 (en-US) delete …
await await …
14 거듭제곱 우결합성 … ** …
13 곱하기 좌결합성 … * …
나누기 … / …
나머지 … % …
12 더하기 좌결합성 … + …
빼기 … - …
11 비트 왼쪽 시프트 좌결합성 … << …
비트 오른쪽 시프트 (en-US) … >> …
비트 부호 없는 오른쪽 시프트 (en-US) … >>> …
10 미만 (en-US) 좌결합성 … < …
이하 (en-US) … <= …
초과 (en-US) … > …
이상 (en-US) … >= …
in … in …
instanceof … instanceof …
9 동등 좌결합성 … == …
부등 … != …
일치 (en-US) … === …
불일치 (en-US) … !== …
7 비트 AND 좌결합성 … & …
7 비트 XOR (en-US) 좌결합성 … ^ …
6 비트 OR (en-US) 좌결합성 … | …
5 논리 AND 좌결합성 … && …
4 논리 OR 좌결합성 … || …
널 병합 연산자 좌결합성 … ?? …
3 조건 (삼항) 우결합성 … ? … : …
2 할당 (en-US) 우결합성 … = …
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
… &&= …
… ||= …
… ??= …
yield 우결합성 yield …
yield* yield* …
1 쉼표 / 시퀀스 좌결합성 … , …