표현식

표현식 (expression)은 값으로 평가될 수 있는 문이다. 즉 결과가 값인 문과, 표현식이 아닌 문의 차이를 이해하는 것이 매우 중요하다. 이 차이를 이해해야만 자바스크립트의 요소들을 효과적으로 조합할 수 있다.

표현식이 아닌 문(statement)은 일종의 지시라고 생각할 수 있고, 표현식은 무언가를 요청하는 것이라고 생각할 수 있습니다. 표현식은 값이 되고 그 결과를 다른 표현식에서 결합해 다른 값을 얻을 수 있다. 표현식이 아닌 문은 이런 식으로 결합할 수 없다.

표현식은 값이 되므로 할당에 쓸 수 있다. 즉, 표현식의 결과를 변수나 상수, 프로퍼티에 할당할 수 있다.

곱셈은 표현식이다.

let x, y;
y = x = 3 * 5;

x와 y 두 변수가 있고 값은 모두 15이다. 이것이 가능한 것은 곱셈과 할당이 모두 표현식이기 때문이다. 자바스크립트는 이렇게 표현식이 결합된 것을 보면 쪼갤 수 있는 만큼 쪼개서 한 부분씩 실행합니다.

자바스크립트가 표현식을 평가하는 순서를 연산자 우선순위라 부르며, 표현식은 대부분 연산자 표현식이다. 즉 곱셈 표현식은 곱셈 연산자 (*)피연산자 (operand) 두 개로 이루어진다.

연산자


연산자를 표현식의 '명사'에 대한 '동사'라고 생각하면 좋다. 표현식이 값이 되는 것이라면 연산자는 값을 만드는 행동이라는 뜻이다.

산술 연산자

자바스크립트의 산술 연산자

    • 덧셈
    • 뺄셈
  • / 나눗셈
    • 곱셈
  • % 나머지
    • 단항 부정
    • 단항 플러스
  • ++ 전위 증가
  • ++ 후위 증가
  • -- 전위 감소
  • -- 후위 감소

연산자 우선순위

연산자 우선순위를 이해해야 자바스크립트 프로그램이 어떻게 동작하는지 이해할 수 있다.

8 / 2 + 3 * (4 * 2 - 1) = 25

괄호가 제일 먼저 계산되고, 그다음 곱셈 나눗셈, 마지막으로 덧셈 뺄샘으로 계산된다.
산수에서만 쓰이는 것은 아니고 자바스크립트는 표현식을 평가할 때도 비슷하게 규칙을 적용한다.

let x = 3, y;
x += y = 6*5/2;

// x = 18
// y = 15

비교 연산자

비교 연산자는 두 개의 값을 비교한다. 크게 말해 비교 연산자는 세 가지 타입으로 나뉜다.

  • 일치함 ( === )
  • 동등함 ( == )
  • 대소관계

일치함은 두 값이 같은 객체를 가리키거나, 같은 타입이고 값도 같다면 이 값을 일치한다고 합니다.
두 값이 일치하는지 확인할 때는 === 연산자를 쓰거나 그 반대인 !== 연산자를 쓴다.

동등함은 두 값이 같은 객체를 가리키거나 같은 값을 갖도록 변환할 수 있다면 두 값을 동등하다고 한다.
앞 부분은 괜찮지만 두 번째 부분은 수많은 골칫거리와 혼란을 발생시킨다. 문자열 "33"은 숫자 33으로 변환할 수 있으므로 이 둘은 동등하다. 하지만 타입이 다르므로 일치하지는 않는다.

const n = 5;
const s = '5';

n === s // false 타입이 다르다.
n !== s // true
n === Number(s) // true

n == s // true 권장하지 않음
n != s // false 권장하지 않음

관계 연산자에는 작다 < , 작거나 같다 <= , 크다 > , 크거나 같다 >= 네 가지가 있다.

숫자비교

숫자를 비교할 때는 염두에 두어야 할 것이 있다.
특별한 숫자형 값 NaN은 그 자신을 포함하여 무엇과도 같지 않다. 즉, NaN === NaN 은 false이다.
숫자가 NaN인지 알아보려면 내장된 isNaN 함수를 사용하면 된다.

자바스크립트의 숫자는 모두 더블 형식이다. 더블 형식은 근사치이므로, 자바스크립트에서 숫자를 비교하다 보면 예상 외의 결과가 나타날 수 있다.

let n = 0;
white(true) {
    n += 0.1;
    if(n === 0.3) break;
}

console.log(n);

이 루프는 0.3에서 멈추지 않고 그 값을 살짝 피한 다음 영원히 실행된다.
이 결과는 0.1이 더블 형식으로 정확히 나타낼 수 없는 값이기 때문인데 0.1은 이진 표현으로 나타낼 수 있는 숫자들 사이에 걸쳐 있다. 따라서 이 루프를 세 번째 반복할 때의 n의 값은 0.3000000000000004이므로 테스트는 false이고, 종료 조건이 실패한다.

Number.EPSILION과 관계 연산자를 사용해서 '느슨하게' 비교하고 성공적으로 루프를 빠져나갈 수 있다.

let n = 0;
white(true) {
    n += 0.1;
    if(Math.abs(n - 0.3) < Number.EPSILION) break;
}

console.log(n);

테스트하는 숫자 n에서 비교 대상(0.3)을 뺀 다음 절댓값을 취하는 방식이다.
여기서 사용한 방법은 두 개의 더블 형식이 같다고 할 수 있을 만큼 가까운 숫자인지 판단할 때 일반적으로 사용하는 방법입니다.

문자열 병합

자바스크립트에서 + 연산자는 덧셈과 문자열 병합에 모두 사용된다.
피연산자의 타입을을 왼쪽에서 오른쪽으로 평가 후 덧셈을 할지 문자열 병합을 할지 판단하고, 피연산자중 하나라도 문자열이면 문자열 병합을 수행한다.

3 + 5 + "8"    // 문자열 '88'
'3' + 5 + 8    //문자열 '358'

논리 연산자

논리 연산자는 불리언 값만 다룰 수 있으며 불리언에는 false / true 두개의 값이 있다.

참 같은 값과 거짓 같은 값

자바스크립트는 모든 데이터 타입을 참 같은 값과 거짓 같은 값으로 나눌 수 있다.

거짓 같은 값

  • undefined
  • null
  • false
  • 0
  • NaN
  • ''(빈 문자열)

이 외의 값은 모두 참 같은 값이다. 굉장히 많이 존재하지만 몇가지 염두에 두어야 한다.

  • 모든 객체, valueOf() 메서드를 호출시 false를 반환하는 객체도 참 같은 값에 속함
  • 배열, 빈 배열
  • 공백만 있는 문자열 (' ')등
  • 문자열 "false"

비구조화 할당

ES6에서 새로 도입된 비구조화 할당은 객체나 배열을 변수로 해채할 수 있다.

const obj = { b: 2, c: 3, d: 4 }

// 비구조화
const { a, b, c } = obj;
a // undefined: obj에는 "a"는 정의되지 않았습니다.
b // 2
c // 3
f // ReferenceError: "f"는 정의되지 않았습니다.

객체를 해체할 때는 반드시 변수 이름과 객체의 프로퍼티 이름이 일치해야한다.

표현식과 흐름 제어 패턴

if...else 문을 3항 연산자로 바꾸기

if(isPrime(n)) {
    label = 'prime';
} else {
    label = 'non-prime';
}

위 코드를 3항 연산자로 바꾸면 코드가 간결해지고 읽기 쉬워진다.

label = isPrime(n) ? 'prime' : 'non-prime';

if 문을 단축 평가하는 OR 표현식으로 바꾸기

if(!options) options = {};

위 코드를 OR 표현식으로 바꿔 간결하게 줄일 수 있다.

options = options || {};

정리


이 연산자들은 데이터를 조작하는 가장 기본적인 방법이다. 비트 연산자처럼 거의 사용하지 않는 연산자도 있고, 점 연산자처럼 연산자라는 생각이 들지 않는 연산자도 있다.

할당, 산술, 비교, 불리언 연산자는 가장 널리 쓰이는 연산자이므로 잘 이해하고 있어야 한다.