let foo = { n: 1 };
let bar = foo; // foo 객체의 참조 주소가 bar 변수에 할당됨
foo.x = foo = { n: 2 }; // 값을 반환하는 할당 연산자
console.log(foo.x) // ?
위 문제를 얼핏보면 foo.x
의 결과는 { n: 2 }
일 것 같지만 아니다. foo.x
는 undefined
를 출력한다. 위 코드의 연산 과정을 하나하나 풀어보면 아래와 같다.
연산 과정을 도식화한 이미지
왼쪽 표현식 평가
우측 표현식이 평가한 값이 할당될 곳을 결정하기 위해 foo.x
가 현재 참조하는 객체 { n: 1 }
확인
오른쪽 표현식 평가
foo
변수가 현재 참조하는 객체 { n: 1 }
확인{ n: 2 }
가 foo
변수에 할당되고 해당 객체 반환우측 표현식이 반환한 { n: 2 }
가 { n: 1 }
객체의 x
속성에 할당
bar
변수는 기존 { n: 1 }
객체를 참조하므로 콘솔 출력 결과는 { n: 1, x: { n: 2 }}
foo
변수엔 { n: 2 }
객체가 할당 됐으므로 foo.x
콘솔 출력 결과는undefined
⭐️ foo.x
가 참조하는 foo
객체 { n: 1 }
는 우측 표현식의 foo
변수가 변경되기 전에 결정되는 것에 주목하자. 할당 연산자는 우결합성(우측부터 계산)을 가지므로 3번째 줄은 아래와 동일하다.
foo.x = (foo = { n: 2 })
하지만 표현식의 평가는 항상 왼쪽부터 이뤄지므로 할당 연산이 이뤄지기 전, 왼쪽 표현식부터 할당될 변수들이 참조하고 있는 값을 평가하고, 우측부터 연산한 값이 할당되는 것.
<aside>
💡 결합성은 우선순위가 동일할 때만 고려된다. (...)
소괄호를 이용한 “그룹” 우선순위가 가장 높다.
</aside>
거듭제곱을 제외한 모든 산술 연산자는 좌(左)결합성(왼쪽 → 오른쪽으로 계산)을 가진다.
(a 연산자1 b) 연산자2 c
1 + 8 + 88 // 연산자 우선순위가 모두 동일하므로 좌결합성 -> (1 + 8) + 88
4 + 5 + "px" // (4 + 5) + "px"
거듭제곱, 할당 연산자, 삼항 연산자는 우(右)결합성(오른쪽 → 왼쪽으로 계산)을 가진다.
a 연산자1 (b 연산자2 c)
2 ** 5 ** 10 // 연산자 우선순위가 모두 동일하므로 우결합성 -> 2 ** (5 ** 10)
let a = b = 8 + 8 // let a = (b = 8 + 8)
연산자의 우선순위가 다르면 결합성과 상관없이 우선순위가 높은 연산자가 먼저 실행된다.
3 + 10 * 2 // 곱셈의 우선순위(15)가 더 높으므로 -> 3 + (10 * 2)
표현식의 평가는 결합성과 무관하게 항상 왼쪽에서 오른쪽으로 진행된다. ⭐️
const echo = (name, num) => {
console.log(name + '항 평가');
return num;
}
console.log(echo("1", 6) ** echo("2", 2) ** echo("3", 3));
// 콘솔 출력 결과 : '1항 평가' -> '2항 평가' -> '3항 평가'
// 표현식의 평가는 왼쪽부터 진행되므로 echo('1', 6)부터 평가한 것
// 연산은 우결합성이므로 오른쪽부터 계산 -> 6 ** (2 ** 3)
<aside>
💡 +
-
=
등 대부분의 자바스크립트 연산자는 값을 반환한다. x = value
를 호출하면 value
가 x
에 할당되고 value
를 반환한다. 연산자 체이닝 등은 이런 특징을 활용한 표현식이다.
</aside>
// 단항 연산자(하나의 피연산자만 받음)
let a = 1;
// 이항 연산자 (두개의 피연산자를 받음)
let a = 1, y = 2; // let a = 1; let y = 2;
// 할당 연산자 체이닝
let a = b = c = 1; // let a = 1; let b = 1; let c = 3;
// 값을 반환하는 할당 연산자
let a = 1;
let b = 2;
let c = 3 - (a = b + 1);
// ⑴ (우측 표현식) b + 1을 평가한 값 3을 a 변수에 할당하고 3 반환
// ⑵ 우측 표현식에서 반환한 값 3을 c 변수에 할당
console.log(a); // 3
console.log(c); // 0