[모던 자바스크립트 Deep Dive] - 7장 정리

2026. 3. 25. 22:40Book Review

반응형

자바스크립트에서 연산자는 하나 이상의 표현식을 대상으로 산술, 할당, 비교, 논리, 타입, 지수 연산 등을 수행해 하나의 값을 만든다. 이때 연산의 대상이 되는 것을 피연산자라고 한다. 피연산자는 값으로 평가될 수 있는 표현식이어야 하고, 피연산자와 연산자의 조합으로 이뤄진 연산자 표현식도 마찬가지로 값으로 평가될 수 있는 표현식이어야 한다.

쉽게 말하면 피연산자가 "값"이라는 명사 역할이라면, 연산자는 그 값을 연산해 새로운 값을 만드는 "동사" 역할이다.

산술 연산자

수학적 계산으로 새로운 숫자값을 만든다. 연산이 불가능하면 NaN을 반환한다. 피연산자 개수에 따라 이항과 단항으로 구분된다.

이항 산술 연산자

2개의 피연산자로 산술 연산을 수행한다. 중요한 건 모든 이항 산술 연산자는 피연산자 값을 변경하는 부수 효과가 없다는 점이다. 어떤 연산을 해도 피연산자의 값은 바뀌지 않고 언제나 새로운 값을 만들어낸다.

10 + 5   // 15
10 - 5   // 5
10 * 5   // 50
10 / 5   // 2
10 % 3   // 1 (나머지)

단항 산술 연산자

1개의 피연산자로 산술 연산을 수행한다. ++, --는 이항과 달리 피연산자 값을 변경하는 부수 효과가 있다. 증가/감소 연산을 하면 암묵적 할당이 이뤄지는 것이다.

let x = 5;

// 전위: 먼저 증가/감소하고 다른 연산 수행
let a = ++x;  // x가 먼저 6이 되고, a에 6 할당
console.log(a, x);  // 6, 6

// 후위: 다른 연산 먼저 수행하고 증가/감소
let b = x++;  // b에 6 먼저 할당하고, x가 7이 됨
console.log(b, x);  // 6, 7

 

+, - 단항 연산자는 피연산자에 아무 효과가 없다. 다만 숫자 타입이 아닌 피연산자에 + 단항 연산자를 쓰면 숫자 타입으로 변환한 값을 반환한다.

+''         // 0
+'1'        // 1
+true       // 1
+false      // 0
+null       // 0
+undefined  // NaN
+'hello'    // NaN

 

부수 효과가 없는 이유는, 피연산자 자체를 바꾸는 게 아니라 변환된 값을 새로 생성해서 반환하기 때문이다. 원본 변수는 그대로다.

let str = '5';
console.log(+str);  // 5 (숫자)
console.log(str);   // '5' (여전히 문자열, 변경 없음)

 

- 단항 연산자는 부호를 반전한 값을 반환한다. 마찬가지로 숫자 타입이 아니면 숫자로 변환 후 부호를 반전한다.

-5      // -5
-(-5)   // 5
-'3'    // -3
-true   // -1

+ 연산자의 이중 역할

+ 연산자는 피연산자 중 하나라도 문자열이면 연결 연산자로 동작한다. 그리고 문자열이 아닌 경우에도 암묵적 타입 변환이 발생하니 주의해야 한다.

1 + '1'    // '11' (문자열 연결)
1 + true   // 2  (true → 1로 변환)
1 + null   // 1  (null → 0으로 변환)
1 + undefined  // NaN (undefined → NaN으로 변환)

 

할당 연산자

우항 피연산자의 평가 결과를 좌항 변수에 할당한다. 변수 값이 변하는 부수 효과가 있다.

let x = 10;
x += 5;   // x = x + 5 → 15
x -= 3;   // x = x - 3 → 12
x *= 2;   // x = x * 2 → 24
x /= 4;   // x = x / 4 → 6
x %= 4;   // x = x % 4 → 2

 

할당문은 표현식처럼 보이지 않지만, 사실 값으로 평가되는 표현식인 문이다. 할당된 값으로 평가된다.

그래서 아래처럼 연쇄 할당이 가능하다.

let a, b, c;
a = b = c = 10;
console.log(a, b, c);  // 10 10 10
// c = 10으로 평가 → b = 10으로 평가 → a = 10으로 평가

 

비교 연산자

좌항과 우항의 피연산자를 비교해 결과를 불리언 값으로 반환한다. if, for 같은 제어문 조건식에 주로 사용된다.

연산자 의미 예시 결과

== 동등 비교 (느슨) 5 == '5' true
=== 일치 비교 (엄격) 5 === '5' false
!= 부동등 비교 5 != '5' false
!== 불일치 비교 5 !== '5' true
> 크다 5 > 3 true
< 작다 5 < 3 false
>= 크거나 같다 5 >= 5 true
<= 작거나 같다 5 <= 3 false

 

동등 비교(==)는 암묵적 타입 변환으로 타입을 맞춘 후 비교하기 때문에 타입이 달라도 true가 나올 수 있다. 결과 예측이 어렵고 실수하기 쉬워서 일치 비교(===)를 쓰는 게 낫다.

5 == '5'    // true  (타입 변환 후 비교)
5 === '5'   // false (타입까지 엄격하게 비교)
0 == false  // true
0 === false // false

 

일치 비교에서 주의할 게 두 가지 있다.

 

첫째, NaN은 자기 자신과도 일치하지 않는 유일한 값이다. NaN인지 확인하려면 Number.isNaN()을 써야 한다.

NaN === NaN       // false (!!!)
Number.isNaN(NaN) // true
Number.isNaN(5)   // false

 

둘째, 자바스크립트에는 +0과 -0이 있는데, ===로 비교하면 true가 나온다. 정확하게 구분하고 싶다면 ES6에서 도입된 Object.is()를 쓰면 된다.

+0 === -0        // true
Object.is(+0, -0)  // false (정확하게 구분)
Object.is(NaN, NaN) // true (NaN도 정확하게 처리)

 

삼항 조건 연산자

조건식의 평가 결과에 따라 반환할 값을 결정한다. 자바스크립트의 유일한 삼항 연산자로 부수 효과는 없다.

조건식 ? 참일 때 반환값 : 거짓일 때 반환값
const score = 75;
const grade = score >= 60 ? '합격' : '불합격';
console.log(grade);  // '합격'

 

조건식이 불리언이 아니면 불리언으로 타입 변환된다.

const val = '' ? '있음' : '없음';  // '' → false로 변환
console.log(val);  // '없음'

 

if/else와의 차이는, 삼항 조건 연산자는 값으로 평가되는 표현식이라 변수에 할당할 수 있지만 if/else는 표현식이 아닌 문이라 변수 할당이 안 된다는 점이다. 단, 조건에 따라 수행해야 할 문이 여러 개라면 if/else가 가독성이 좋다.

// 삼항 — 값으로 평가되므로 변수에 할당 가능
const result = x > 0 ? '양수' : '음수 또는 0';

// if/else — 변수 할당 불가, 하지만 복잡한 로직엔 더 적합
if (x > 0) {
  console.log('양수');
  doSomething();
} else {
  console.log('음수 또는 0');
  doSomethingElse();
}

 

논리 연산자

연산자 의미 예시

|| 논리합 (OR) true || false → true
&& 논리곱 (AND) true && false → false
! 논리부정 (NOT) !true → false

 

논리 부정(!)은 언제나 불리언 값을 반환한다. 피연산자가 불리언이 아니면 암묵적으로 변환된 후 부정한다.

!true     // false
!false    // true
!0        // true  (0 → false → 부정 → true)
!''       // true
!'hello'  // false
!null     // true

 

논리합(||)과 논리곱(&&)은 결과가 불리언이 아닐 수 있다. 언제나 2개의 피연산자 중 어느 한쪽으로 평가된다. (단축 평가에 관한 내용은 타입 변환과 단축 평가 글에서 자세히 다뤘다.)

'cat' || 'dog'   // 'cat'  (좌항이 truthy라 바로 반환)
false || 'dog'   // 'dog'  (좌항이 falsy라 우항 반환)
'cat' && 'dog'   // 'dog'  (좌항이 truthy라 우항이 결과 결정)
false && 'dog'   // false  (좌항이 falsy라 바로 반환)

 

드 모르간의 법칙도 알아두면 복잡한 논리 조건을 단순화할 때 유용하다. 부정 논리를 괄호 안으로 분배할 수 있다는 법칙이다.

// 드 모르간 법칙
!(x || y) === (!x && !y)
!(x && y) === (!x || !y)

// 실제 적용 예
!(true || false)   // false
(!true && !false)  // false  ← 동일한 결과

!(true && false)   // true
(!true || !false)  // true   ← 동일한 결과

 

기타 연산자

쉼표 연산자

왼쪽 피연산자부터 차례대로 평가하고 마지막 피연산자의 평가 결과를 반환한다.

let x = (1, 2, 3);
console.log(x);  // 3 (마지막 값 반환)

for (let i = 0, j = 10; i < 5; i++, j--) {
  console.log(i, j);  // 0 10, 1 9, 2 8 ...
}

그룹 연산자

소괄호로 피연산자를 감싸 먼저 평가한다. 연산자 우선순위를 직접 조절할 수 있다.

2 + 3 * 4    // 14 (곱셈 먼저)
(2 + 3) * 4  // 20 (그룹 먼저)

typeof 연산자

피연산자의 데이터 타입을 문자열로 반환한다. 반환되는 문자열은 'string', 'number', 'boolean', 'undefined', 'symbol', 'object', 'function' 7가지다.

주의할 게 두 가지 있다.

 

첫째, null을 typeof로 확인하면 'null'이 아니라 'object'가 반환된다. 기존 코드에 영향을 줄까봐 수정되지 않고 있는 버그다. null을 확인할 때는 반드시 일치 연산자(===)를 써야 한다.

typeof null   // 'object' (!!!)

// null 확인은 이렇게
const val = null;
val === null   // true (올바른 방법)

 

둘째, 선언하지 않은 식별자를 typeof로 확인하면 ReferenceError가 발생할 것 같지만 실제로는 'undefined'를 반환한다. 실수하기 쉬운 부분이니 주의해야 한다.

typeof undeclaredVar   // 'undefined' (에러가 아님!)

지수 연산자

ES7에서 도입됐다. 좌항을 밑, 우항을 지수로 거듭제곱한 값을 반환한다. 도입 전에는 Math.pow()를 사용했는데, 체이닝이 필요한 경우에는 오히려 Math.pow()가 가독성이 좋을 때도 있다.

2 ** 3    // 8
2 ** 10   // 1024

// Math.pow를 쓰는 게 나은 경우 — 여러 번 중첩될 때
Math.pow(Math.pow(2, 3), 2)  // 64
(2 ** 3) ** 2                // 64 (같은 결과지만 괄호가 많아짐)

 

음수를 밑으로 사용하려면 괄호로 묶어야 한다.

(-2) ** 3   // -8

 

할당 연산자와 함께 쓸 수도 있다.

let x = 2;
x **= 3;
console.log(x);  // 8

그 외 연산자

  • ?. (옵셔널 체이닝) — 좌항이 null/undefined면 undefined 반환
  • ?? (null 병합) — 좌항이 null/undefined면 우항 반환
  • instanceof — 객체가 특정 생성자의 인스턴스인지 확인
  • in — 객체 안에 특정 프로퍼티가 있는지 확인
  • delete — 객체의 프로퍼티를 삭제 (부수 효과 있음)

부수 효과가 있는 연산자

대부분의 연산자는 다른 코드에 영향 없이 결과값만 새로 만들지만, 아래 세 가지는 부수 효과가 있다.

  • = (할당 연산자) — 변수의 값이 바뀜
  • ++, -- (증가/감소) — 피연산자 값이 직접 바뀜
  • delete — 객체의 프로퍼티가 삭제됨

연산자 우선순위

여러 연산자가 섞인 표현식에서 어떤 순서로 실행되는지를 결정한다.

1 (높음) () 그룹
2 ++, -- (후위), !, +, - (단항)
3 **
4 *, /, %
5 +, -
6 <, <=, >, >=
7 ==, !=, ===, !==
8 &&
9 ||
10 ??
11 (낮음) =, +=, -= 등 할당

 

솔직히 이거 다 외우는 건 무리다. 헷갈리면 그냥 그룹 연산자 ()로 직접 묶어서 명시적으로 순서를 정하는 게 낫다.

연산자 결합 순서

연산자 결합 순서는 동일한 우선순위의 연산자가 여러 개일 때 좌항부터 평가할지, 우항부터 평가할지를 결정한다.

좌 → 우 +, -, *, /, %, <, >, ==, !=, ===, !==, &&, ||
우 → 좌 =, +=, -=, **, !, ++, -- (전위)
// 좌 → 우 결합 (덧셈)
1 + 2 + 3   // (1 + 2) + 3 = 6

// 우 → 좌 결합 (지수)
2 ** 3 ** 2   // 2 ** (3 ** 2) = 2 ** 9 = 512

// 우 → 좌 결합 (할당)
a = b = 5   // a = (b = 5)
반응형