우리가 작성하는 모든 자바스크립트 코드는 모두 문장과 표현식으로 구성되어 있다.
자바스크립트에서 문장은 어떤 동작이 일어나도록 작성된 최소한의 코드 덩어리를 가리킨다.
let x;
x = 3;
if (x < 5) {
console.log('x는 5보다 작다');
} else {
console.log('x는 5와 같거나 크다');
}
for (let i = 0; i < 5; i++) {
console.log(i);
}
이 코드의 첫 번째 줄도 x라는 변수를 선언하는 동작이 일어나는 하나의 문장이고, 두 번째 줄도 x에 3이라는 값을 할당하는 동작이 일어나는 하나의 문장이다. 선언문, 할당문, 조건문, 반복문.. 이렇게 끝에 문이라고 붙은 이유가 모두 동작을 수행하는 문장이기 때문이다.
표현식은 결과적으로 하나의 값이 되는 모든 코드를 가리킨다.
5 // 5
'string' // string
이렇게 어떤 하나의 값을 그대로 작성하는 것도 표현식이지만,
5 + 7 // 12
'I' + 'Love' + 'Codeit' // I Love Codeit
true && null // null
연산자를 이용한 연산식도 결국은 하나의 값이 되고,
const title = 'JavaScript';
const codeit = {
name: 'Codeit'
};
const numbers = [1, 2, 3];
typeof codeit // object
title // JavaScript
codeit.name // Codeit
numbers[3] // undefined
위 코드의 마지막 네 줄처럼 선언된 변수를 호출하거나, 객체의 프로퍼티에 접근하는 것도 결국에는 하나의 값으로 평가된다. 그래서 길이와는 상관없이 결과적으로 하나의 값이 되는 코드를 모두 표현식이라고 할 수가 있다.
표현식은 보통 문장의 일부로 쓰이지만, 그 자체로 문장일 수도 있다. 가장 대표적인 예시가 할당식과 함수 호출이다.
// 할당 연산자는 값을 할당하는 동작도 하지만, 할당한 값을 그대로 가지는 표현식이다.
title = 'JavaScript'; // JavaScript
// 함수 호출은 함수를 실행하는 동작도 하지만, 실행한 함수의 리턴 값을 가지는 표현식이다.
SayHi(); // sayHi 함수의 리턴 값
// console.log 메소드는 콘솔에 아규먼트를 출력하는 동작도 하지만, undefined 값을 가지는 표현식이다.
console.log('hi'); // undefined
사실은 할당연산자 자체가 할당한 값을 그대로 리턴하는 특징이 있기 때문에 연산 자체로 값이 되는 표현식이기도 하다. 그런데 할당식은 왼쪽에 있는 피연산자에 오른쪽 피연산자 값을 할당하는 동작을 하기 때문에, 문장이 되기도 한다. 그리고 함수 호출도 함수를 호출한 자리가 결국에는 하나의 리턴하는 값을 가지기 때문에 표현식이라고 할 수도 있지만 함수 내부에 정의한 코드를 실행하는 동작이기 때문에 문장이 되기도 하는 것이다.
결과적으로 문장은 다시 표현식인 문장과, 표현식이 아닌 문장으로 나눌 수 있다. 이 둘을 구분하는 가장 간단한 방법은 우리가 구분하고자 하는 문장을 변수에 할당하거나, 어떤 함수의 아규먼트로 전달해보는 것이다.
let x;
x = 3;
console.log(if (x < 5) {
console.log('x는 5보다 작다');
} else {
console.log('x는 5보다 크다');
});
const someloop = for (let i = 0; i < 5; i++) {
console.log(i);
};
console.log 메소드의 아규먼트로 if문을 전달하거나 someloop 라는 변수에 for 반복문을 할당하게 되면, Error가 발생하게 된다. 조건문이나 반복문은 값으로 평가되지 않고 오로지 문장으로만 평가되기 때문이다.
*마지막으로
(3 + 4) * 2;
console.log('Hi!');
while(true) {
x++;
}
자바스크립트에서 특별한 경우를 제외하면 일반적으로 표현식인 문장은 세미콜론으로, 표현식이 아닌 문장은 문장 자체의 코드 블록(중괄호)로 그 문장의 범위가 구분된다.
ES2015에는 자바스크립트에서 조건을 다루는 문법이 if문과 switch문 두 가지 뿐이었다.
if (조건) {
// 조건이 true일 때 동작
} else {
// 조건이 false일 때 동작
}
switch (값) {
case A:
// 값이 A와 일치할 때 동작
break;
default:
// 값과 일치하는 case가 없을 때 동작
}
조건 연산자를 활용하면 더 간결하게 표현식으로도 조건을 다룰 수가 있다. 이는 if문과 동작하는 방식이 굉장히 비슷하다. 이는 삼항 연산자라는 이름으로 불리기도 한다.
조건 ? truthy 할 때 표현식 : falsy 할 때 표현식
삼항 연산자 (Ternary operator)
const CUT_OFF = 80;
function passChecker(score) {
return score > CUT_OFF ? '합격!' : '불합격!';
}
console.log(passChecker(75));
}
조건 연산자가 if문보다 간결하고 유연한 것처럼 보이긴 하지만 조건 연산자는 표현식이기 때문에 조건에 따라 변수를 선언한다거나 조건에 따라 반복문을 실행할 수는 없다. 그래서 조건 연산자가 모든 if문을 대체할 수 없다는 한계도 참고해야 한다.
spread 구문은 배열을 다룰 때 유용하게 사용된다. "펼치다" 라는 spread의 의미처럼 이렇게 여러 개의 값을 하나로 묶은 배열을 다시 각각의 개별 값으로 펼치는 문법이다.
문법은 배열 앞에 마침표 세 개를 붙여주는 것이다.
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr3 = [...arr1, ...arr2];
console.log(arr3);
Rest Parameter는 여러 개의 아규먼트를 하나의 파라미터로 묶는 방식이고, Spread 구문은 하나로 묶여 있는 값을 각각의 개별 값으로 펼치는 방식이다.
Spread 구문을 활용하면 배열을 좀 더 편리하게 복사할 수 있다. 객체 타입의 값들은 변수에 값이 직접 할당되는 게 아니라 주솟값이 참조된다. Spread 구문을 활용하면 메소드를 사용하지 않고도 배열을 복사할 수 있다. 때문에 이를 잘 활용하면 좀 더 직관적이고 간단하게 배열을 다룰 수 있게 된다.
이뿐만 아니라 함수를 호출할 때 아규먼트로도 활용할 수가 있다. 여러 개의 파라미터가 있는 함수를 호출할 때 배열을 펼쳐서 각각의 아규먼트로 사용할 수 있다.
주의할 것은 Spread 구문을 값으로 보면 안된다는 점이다. Spread 구문은 하나로 묶인 값을 여러 개의 값으로 펼친 개념이기 때문에 여러 값의 목록으로 평가된다. 그래서 하나의 값을 가진 배열을 펼친다면 오류가 발생한다.
Spread 구문으로 배열을 펼친 다음에 중괄호로 감싸서 객체로 만들게 되면 0번 부터 시작하는 배열의 인덱스가 프로퍼티 네임이 되어서 객체가 만들어진다.