
자바스크립트에서 표현식이란 건 값 하나 쥐어짜내는 제일 작은 단위다. 연산자는 그 값들 억지로 붙여서 결과를 갈겨낸다. 어떤 건 값만 던지고 끝나지만, 어떤 건 부수효과까지 싸질러서 코드판을 싹 다 개판으로 만들어버린다.
이번 장에서는 자주 쓰이는 연산자와 표현식이 어떻게 굴러가는지, 누가 먼저 치고 나가고 어떻게 붙어 쳐먹는지까지 싸그리 까발려 보겠다.
x = 7 → x에 7을 대입하고, 전체 표현식의 값도 7.3 + 4 → 7을 만들지만, 어딘가에 대입하지 않으면 즉시 버려진다.연산자는 =, +처럼 표현식을 연결한다. 이때 우선순위와 결합성을 이해하면 평가 순서를 예측할 수 있다.
const x = 1 + 2 * 3; // 7, *가 +보다 먼저
const y = (1 + 2) * 3; // 9, 괄호로 우선순위 재정의
=)은 오른쪽 결합이다. y = x = f()는 y = (x = f())와 같다.a + b, x * y (모두 중위 표기)++x(접두), x++(접미). 접미는 ++, -만 가능.cond ? a : b가장 기본은 =이며, 축약형이 다수 존재한다.
| 이름 | 축약 | 의미 |
|---|---|---|
| 단순 대입 | x = f() | x = f() |
| 더하기 대입 | x += f() | x = x + f() |
| 빼기/곱/나누기/나머지 | -=, *=, /=, %= | 각각 |
| 제곱 | **= | x = x ** f() |
| 비트 시프트/비트 연산 | <<=, >>=, >>>=, &=, ^=, ` | =` |
| 논리 대입 | &&=, ` |
const obj = {};
obj.x = 3; // { x: 3 }
const k = "y";
obj[k] = 5; // { x: 3, y: 5 }
원시값에는 프로퍼티를 쓸 수 없다(엄격 모드에서는 에러).
const [a, b] = [10, 20];
const {name, age: years} = { name: "Ari", age: 30 };
대입식 자체가 값을 반환한다.
let x;
const y = (x = f()); // x와 y 모두 f()의 결과
console.log(x = f()); // 대입 결과를 바로 로그
const z = y = x = f();
// 실제로는 z만 선언되고, x/y는 전역 유출(슬로피 모드) 또는 에러(스트릭트)
== / 부등 != : 필요 시 타입 변환 수행=== / 부등 !== : 타입까지 같아야 참>, >=, <, <= : 숫자 비교(문자열은 유니코드 사전순)3 == "3"; // true
3 === "3"; // false
"12" > 2; // true (숫자 변환 후 비교되는 경우가 많음)
메모: =>는 비교 연산자가 아니라 화살표 함수 표기다.
표준 연산 + - * / 외에 다음이 있다.
12 % 5; // 2 (나머지)
++x; x++; // 증가 (접두/접미)
--x; x--; // 감소
-x; // 부호 반전
+"3"; // 3 (단항 +는 수 변환)
2 ** 3; // 8 (지수)
0으로 나누면 Infinity(부동소수점 규칙).
32비트 정수로 변환해 비트 단위로 연산한다.
&(AND), |(OR), ^(XOR), ~(NOT)<<, >>(부호 유지), >>>(제로 필)15 & 9; // 9 (1111 & 1001 = 1001)
15 ^ 9; // 6 (0110)
~15; // -16 // ~x === -x - 1
9 << 2; // 36
-9 >> 2; // -3 (부호 전파)
&&, ||, ??, !
&& anything → falsy|| anything → truthy?? anything → 앞의 값"Cat" && "Dog"; // "Dog"
false || "Cat"; // "Cat"
null ?? "N/A"; // "N/A"
0 ?? 42; // 0 (||와의 중요한 차이점)
!true; // false
대부분의 수 연산/비트 연산을 지원한다(단, >>> 없음).
1n + 2n; // 3n
1n / 2n; // 0n (0으로 내림)
40000000000000000n >> 2n; // OK
// 혼용 금지
// 1n + 2; // TypeError
Number(1n) + 2; // 3 (명시 변환)
+, +="my " + "string"; // "my string"
let s = "alpha";
s += "bet"; // "alphabet"
const status = age >= 18 ? "adult" : "minor";
간단 분기에는 if…else보다 읽기 좋다.
두 피연산자를 평가하고 마지막 값을 반환. 주로 for의 헤더에서 다중 업데이트에 사용. 일반 코드에서는 가독성 때문에 지양.
for (let i = 0, j = 9; i <= j; i++, j--) {
console.log(i, j);
}
delete객체의 프로퍼티를 제거한다. 성공 시 true.
const o = { h: 4 };
delete o.h; // true
delete Math.PI; // false (비구성 프로퍼티)
배열 요소를 delete하면 구멍(hole)만 남고 길이는 그대로다. splice 등 배열 메서드를 사용하자.
typeof피연산자의 타입을 문자열로 반환.
typeof (() => 7); // "function"
typeof 62; // "number"
typeof null; // "object" // 역사적 이유
typeof foo; // "undefined" (선언 전 식별자)
void표현식을 평가만 하고 값을 버림.
in프로퍼티 존재 여부(키 기준). 배열에는 인덱스로 검사.
"PI" in Math; // true
const trees = ["redwood","bay"];
0 in trees; // true
"bay" in trees; // false (값이 아니라 키 검사)
instanceof생성자 기반 타입 체크.
const m = new Map();
m instanceof Map; // true
this메서드 호출 시 수신 객체를 가리키는 암묵 인자.
function getFullName() { return `${this.first} ${this.last}`; }
const p = { first: "Chris", last: "Martin", getFullName };
p.getFullName(); // "Chris Martin"
()우선순위 제어.
(1 + 2) * 3; // 9
점 표기/대괄호 표기.
obj.prop; obj["prop"];
?.null/undefined이면 짧게 끊고 undefined 반환.
user.profile?.name;
maybeFn?.();
new생성자 호출로 인스턴스 생성.
const d = new Date();
super상위(부모) 생성자/메서드 호출(클래스 문맥).
super(args);
super.method();
== 대신 ===, 기본값에는 ??, 배열 순회에는 delete 지양.요약하면, 연산자는 값 선택과 제어의 언어다. 규칙(우선순위·결합성)과 각 연산자의 미묘한 차이만 챙겨두면, 의도가 선명한 표현식을 만들 수 있다.