JavaScript 연산자 심층 분석: 할당, 산술, 비교, 논리 및 비트 연산

1. 할당 연산자 (Assignment Operators)

할당 연산자는 우변의 평가 결과를 좌변의 변수나 프로퍼티에 바인딩하는 역할을 합니다. 기본적으로 우항에서 좌항으로 평가가 이루어집니다.

let baseScore = 100;        // 우변의 100을 좌변 baseScore에 할당
const config = {};
config.timeout = 5000;      // 객체의 프로퍼티에 값 할당

연쇄 할당을 사용할 경우, 가장 오른쪽의 할당부터 순차적으로 실행됩니다. 이때 주의할 점은 할당의 타겟이 되는 좌변은 반드시 값을 저장할 수 있는 참조(변수, 프로퍼티 등)여야 한다는 것입니다.

let x, y;
x = y = 42;               // y에 42 할당 후, 그 결과인 42를 x에 할당

// SyntaxError: Invalid left-hand side in assignment
// let invalid = 50 = 60; // 리터럴 50은 좌변이 될 수 없음

2. 산술 연산자 (Arithmetic Operators)

2.1 단항 산술 연산자

단항 연산자는 하나의 피연산자를 대상으로 하며, 양수(+), 음수(-), 증가(++), 감소(--) 기호가 있습니다. 특히 단항 +는 피연산자를 숫자형으로 강제 변환하는 트릭으로 자주 활용됩니다.

const strNum = "123";
const boolVal = true;

console.log(+strNum);   // 123 (숫자형으로 변환)
console.log(+boolVal);  // 1
console.log(-"45.6");   // -45.6

2.2 이항 산술 연산자

이항 연산자는 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/), 나머지(%), 거듭제곱(**)을 수행합니다.

덧셈과 타입 변환

덧셈 연산자는 피연산자의 타입에 따라 숫자 연산 또는 문자열 결합으로 동작합니다. 하나라도 문자열이면 문자열 결합이 우선되며, 그 외의 타입은 숫자로 변환되어 연산됩니다.

console.log(15 + 25);          // 40
console.log("Score: " + 99);   // "Score: 99"
console.log(10 + true);        // 11 (true는 1로 변환)
console.log(10 + null);        // 10 (null은 0으로 변환)
console.log(10 + undefined);   // NaN (undefined는 NaN으로 변환)
console.log(5 + [1, 2]);       // "51,2" (배열은 문자열로 변환 후 결합)

뺄셈, 곱셈, 나눗셈

덧셈을 제외한 나머지 산술 연산자는 피연산자를 무조건 숫자로 변환합니다. 변환이 불가능한 경우 NaN을 반환합니다. JavaScript의 나눗셈은 정수 나눗셈이 아닌 부동소수점 연산을 수행하며, 0으로 나눌 때 예외를 발생시키지 않고 Infinity를 반환합니다.

console.log(20 - "5");       // 15
console.log(8 * null);       // 0
console.log(10 / "abc");     // NaN
console.log(7 / 2);          // 3.5 (부동소수점 결과)
console.log(5 / 0);          // Infinity
console.log(-5 / 0);         // -Infinity
console.log(10 / -0);        // -Infinity (음수 0으로 나눌 경우)

나머지 연산과 거듭제곱

나머지(%) 연산은 부동소수점에도 적용 가능하며, 순환 인덱스 등을 구현할 때 유용합니다. 거듭제곱(**)은 ES2016에서 도입되었으며, 우결합성을 가집니다.

const items = ['A', 'B', 'C'];
let idx = 4;
console.log(items[idx % items.length]); // 'B' (4 % 3 = 1)

console.log(2 ** 10);      // 1024
console.log(4 ** 0.5);     // 2 (제곱근)
console.log(2 ** -1);      // 0.5 (역수)

3. 비교 연산자 (Comparison Operators)

비교 연산자는 두 피연산자의 관계를 평가하여 불리언(true 또는 false) 값을 반환합니다.

3.1 관계 연산자

크기 비교(>, <, >=, <=)는 기본적으로 숫자형으로 변환 후 평가됩니다. 단, 양쪽 모두 문자열인 경우 UTF-16 코드 포인트 순서대로 사전식 비교를 수행합니다.

console.log(100 > "50");      // true ("50"이 50으로 변환됨)
console.log("apple" < "banana"); // true ('a'의 코드포인트가 'b'보다 작음)
console.log("10" < "2");      // true (문자열 비교이므로 '1'이 '2'보다 작음)
console.log(10n > 5);         // true (BigInt와 Number 간 비교 가능)

3.2 동등 연산자

동등(==) 연산자는 타입이 다를 경우 암묵적 타입 변환을 수행한 후 값을 비교합니다. 이로 인해 예기치 않은 결과가 발생할 수 있습니다. 반면, 일치(===) 연산자는 타입과 값이 모두 동일해야 true를 반환하는 엄격한 비교를 수행합니다.

console.log(0 == false);       // true
console.log(null == undefined); // true
console.log("10" == 10);       // true

// 엄격한 비교 권장
console.log(0 === false);       // false
console.log(null === undefined); // false
console.log("10" === 10);       // false

참고: NaN은 자기 자신을 포함하여 어떤 값과도 == 또는 ===로 비교 시 false를 반환합니다. NaN 여부는 Number.isNaN()으로 확인해야 합니다.

4. 논리 연산자 (Logical Operators)

논리 연산자는 논리곱(&&), 논리합(||), 논리부정(!)을 수행합니다. JavaScript에서 논리 연산자는 반드시 불리언을 반환하는 것이 아니라, 단락 평가(Short-circuit evaluation)에 따라 피연산자 중 하나를 그대로 반환할 수 있습니다.

const user = { name: "Alice", role: null };

// 논리곱: 첫 번째 Falsy 값을 반환하거나 마지막 Truthy 값 반환
console.log(user && user.name); // "Alice"

// 논리합: 첫 번째 Truthy 값을 반환하거나 마지막 Falsy 값 반환
const defaultRole = "guest";
console.log(user.role || defaultRole); // "guest"

// 논리부정: 피연산자를 불리언으로 변환한 후 반전
console.log(!"");    // true
console.log(!100);   // false

5. Null 병합 연산자 (Nullish Coalescing)

ES2020에서 도입된 ?? 연산자는 좌변이 null 또는 undefined일 때만 우변의 값을 반환합니다. 논리합(||) 연산자가 0, "", false와 같은 Falsy 값도 대체해 버리는 문제를 해결합니다.

const settings = {
    volume: 0,
    theme: "",
    timeout: null
};

console.log(settings.volume ?? 50);    // 0 (0은 nullish가 아님)
console.log(settings.theme ?? "dark"); // "" (빈 문자열은 nullish가 아님)
console.log(settings.timeout ?? 3000); // 3000 (null이므로 우변 반환)

6. 삼항 연산자 (Ternary Operator)

조건부 연산자라고도 불리는 삼항 연산자는 condition ? exprIfTrue : exprIfFalse 형태로, 간단한 조건부 로직을 표현할 때 사용됩니다.

const age = 20;
const status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"

const score = 85;
const grade = score >= 90 ? "A" : score >= 80 ? "B" : "C";
console.log(grade);  // "B" (중첩 삼항 연산자)

7. 비트 연산자 (Bitwise Operators)

비트 연산자는 피연산자를 32비트 부호 있는 정수(또는 부호 없는 정수)로 변환한 후, 이진 비트 단위로 연산을 수행합니다. 부동소수점은 정수 부분만 취해 32비트 정수로 변환됩니다.

7.1 비트 논리 연산

  • 비트 AND (&): 두 비트가 모두 1일 때 1을 반환합니다.
  • 비트 OR (|): 두 비트 중 하나라도 1이면 1을 반환합니다.
  • 비트 XOR (^): 두 비트가 다를 때 1을 반환합니다.
  • 비트 NOT (~): 모든 비트를 반전시킵니다. ~x-(x + 1)과 동일한 결과를 냅니다.
console.log(5 & 3);  // 1  (0101 & 0011 = 0001)
console.log(5 | 3);  // 7  (0101 | 0011 = 0111)
console.log(5 ^ 3);  // 6  (0101 ^ 0011 = 0110)
console.log(~5);     // -6 (5의 비트 반전 + 1의 보수)

7.2 비트 시프트 연산

비트를 왼쪽 또는 오른쪽으로 이동시킵니다. 이동 후 발생하는 빈자리는 채워지는 방식에 따라 결과가 달라집니다.

  • 왼쪽 시프트 (<<): 비트를 왼쪽으로 이동하고, 빈자리는 0으로 채웁니다.
  • 부호 전파 오른쪽 시프트 (>>): 비트를 오른쪽으로 이동하고, 빈자리는 기존 최상위 비트(부호 비트)로 채웁니다.
  • 0 채움 오른쪽 시프트 (>>>): 비트를 오른쪽으로 이동하고, 빈자리를 무조건 0으로 채워 부호 없는 정수로 만듭니다.
console.log(4 << 1);   // 8  (0100 << 1 = 1000)
console.log(-8 >> 1);  // -4 (부호 비트 1이 유지됨)
console.log(-8 >>> 1); // 2147483644 (빈자리가 0으로 채워져 양수가 됨)

8. 복합 할당 연산자 (Compound Assignment)

산술, 비트, 논리 연산 등을 할당 연산과 결합하여 코드를 간결하게 작성할 수 있습니다. x += yx = x + y와 동일하게 동작합니다.

let counter = 10;
counter += 5;   // 15
counter *= 2;   // 30
counter >>= 1;  // 15 (비트 오른쪽 시프트 후 할당)
counter &&= 20; // 20 (논리곱 할당: counter가 truthy이므로 20 할당)

9. 연산자 우선순위 (Operator Precedence)

하나의 표현식에 여러 연산자가 혼재될 경우, 우선순위가 높은 연산자가 먼저 평가됩니다. 동일한 우선순위를 가진 연산자들은 결합성(좌결합 또는 우결합)에 따라 평가 순서가 결정됩니다. 예를 들어, 할당 연산자는 우결합성을 가지며, 대부분의 산술 및 논리 연산자는 좌결합성을 가집니다. 복잡한 표현식에서는 가독성과 의도치 않은 버그를 방지하기 위해 괄호 ()를 사용하여 평가 순서를 명시적으로 지정하는 것이 좋습니다.

태그: JavaScript 연산자 비트연산 ES2020 NullishCoalescing

6월 13일 03:52에 게시됨