러닝 자바스크립트 5장에 해당되는 부분이며, 읽으면서 자바스크립트에 대해 새롭게 알게된 것과 기록하고 싶은 부분을 정리한 내용입니다.
표현식은 결과가 값인 문으로, 값으로 평가될 수 있는 문이다. 표현식과 표현식이 아닌 문의 차이는 결과를 반환하는 것에 있다. 표현식은 값이기 때문에 그 결과를 다른 표현식에 결합해서 다른 값을 얻을 수도 있다.
let x;
x = 3 * 5;
두 번째 행에는 표현식이 두 개가 있는데 첫 번째 표현식은 3 * 5
곱셈 표현식이다. 이 표현식의 결과는 15이고, 두 번째 표현식인 =
할당 표현식에 사용되는 것이다.
여기서 x만 값 15를 할당 받는 것이 아니라, 전체 표현식의 결과도 값 15이기 때문에 할당 표현식도 값이 된다.
let x, y;
y = x = 3 * 5;
// 1️⃣ y = x = 15; 곱셈 표현식 계산
// 2️⃣ y = 15; 첫 번째 할당(x = 15) 계산
// 3️⃣ 15; 두 번째 할당(y = 15) 계산
곱셈과 할당이 모두 표현식이기 때문에 자바스크립트는 표현식이 결합된 것을 보면 쪼갤 수 있는 만큼 쪼개서 한 부분씩 실행하게 된다.
표현식은 대부분 연산자 표현식이다. 즉 곱셈 표현식은 곱셈 연산자와 피연산자 두 개로 이루어진다. 여기서 피연산자는 서로 곱하는 두 숫자이며 피연산자 자체도 표현식이다.
연산자가 아닌 표현식에는 식별자 표현식(변수, 상수의 이름)과 리터럴 표현식 두 가지가 있다.
표현식이 값이 되는것이라면 연산자는 값을 만드는 행동이라고 할 수 있다.
const x = 5;
const y = 3 - -x; // 8
산술 연산자 중 단항 부정(-)
과 단항 플러스(+)
라는 연산자가 존재한다. 각각 뺄셈 연산자와 덧셈 연산자의 기호와 동일한데 연산의 순서는 단항 연산자가 먼저 이루어지고 덧셈(뺄셈)이 이루어진다. 단항 플러스
연산자는 보통 문자열을 숫자로 강제 변환하는 경우에 사용된다.
const s = "5";
const y = 3 + +s; // 8
단항 플러스를 사용하지 않았다면 결과는 35가 된다.
비교 연산자는 크게 일치(===)
, 동등(==)
, 대소 관계의 세 가지 타입으로 나뉜다.
일치와 동등의 차이는 비교하고자 하는 두 값이 같은 객체를 기리키거나, 같은 타입이고 값도 같다면 이 값을 일치
한다고 한다.
동등
은 두 값이 같은 객체를 가리키거나 같은 값을 갖도록 변환할 수 있을 때 사용한다.
동등의 두 번째 부분은 아래의 코드로 이해할 수 있을 것 같다.
const a = "33";
const b = 33;
console.log(a == b); // true, 문자열 a는 숫자로 변환할 수 있으므로 동등하다.
저자는 동등 비교가 편리할 때도 있지만, 편리함 때문에 수많은 부작용이 생길 수 있으므로 문자열은 미리 숫자로 변환해서 일치 비교를 사용하라고 권장한다.
동등 연산자 때문에 문제가 생기는 경우는 null, undefined, 빈 문자열, 숫자 0 때문인데 네 가지 경우에 해당하지 않는 값을 비교할 때 일지라도 일치 비교를 사용한다면 이러한 문제에 대해 생각할 필요도 없기 때문에, 데이터 타입을 변환해서 그냥 일치 비교를 사용하라고 말한다.
자바스크립트에서 특별한 숫자형 값 NaN
은 자신을 포함하여 무엇과도 같지 않다.
console.log(Nan === Nan); // false
console.log(Nan == Nan); // false
숫자가 NaN인지 알아보려면 내장된 함수인 isNaN()
함수를 사용한다.
자바스크립트에서 정수를 비교할 때, Number.MIN_SAFE_INTEGER
=> (-(2^53 - 1)
)과 Number.MAX_SAFE_INTEGER
=> (2^53 - 1)
사이의 수, 즉 안전하게 표현할 수 있는 범위에 있다면 안심하고 일치 연산자를 사용할 수 있다. 이 이유는 자바스크립트가 IEEE 754에 기술된 배정밀도 부동소숫점 형식 숫자체계를 사용하기 때문이다.
이외의 범위나 근접한 값을 사용할 때는 Number.isSafeInteger
을 통해 검사해서 사용해야 한다.
하지만 소수점이 있는 숫자를 비교할 때는 특별한 숫자형 상수 Number.EPSILON
을 사용해 숫자 두 개를 구별하는 기준으로 사용해 느슨하게 비교한다.
let n = 0;
while(true) {
n += 0.1;
if(n === 0.3) break;
}
console.log('stop');
a가 0.2인 상태에서 0.1을 더하게 되면 0.3이 되어 루프를 빠져 나올 것으로 기대하지만 0.1은 더블 형식으로 정확히 나타낼 수 없는 값이기 때문에 0.30000000000000004과 같은 값이 되어 무한 루프에 빠지게 된다.
Number.EPSILON
관계 연산자를 사용해 느슨하게 비교해 루프를 빠져나갈 수 있다.
let n = 0;
while(true) {
n += 0.1;
if(Math.abs(n - 0.3) < Number.EPSILON) break;
}
console.log('stop');
자바스크립트에서 + 연산자는 덧셈과 문자열 병합에 사용되는데, 피연산자의 타입을 보고 덧셈을 할지 문자열 병합을 할지 판단한다. 덧셈과 문자열 병합 모두 왼쪽에서 오른쪽으로 진행되며, 피연산자 중 하나라도 문자열이라면 문자열 병합을 수행하게 된다.
3 + 5 + "8" // 8 + "8" = "88"
"3" + 5 + 8 // "35" + 8 = "358"
대부분의 프로그래밍 언어에서 논리 연산자는 불리언 값만 반환하지만, 자바스크립트의 논리 연산자는 불리언이 아닌 값도 다룰 수 있고, 불리언이 아닌 값을 반환하기도 한다.
그렇다고 자바스크립트의 논리 연산자가 잘못 만들어 진게 아님. 불리언 값에 사용하면 불리언 값을 반환한다.
자바스크립트에서는 모든 데이터 타입을 참 같은 값
과 거짓 같은 값
두 가지 종류로 나눌 수 있다.
위와 같은 값 이외에는 모두 참 같은 값이다.
AND 연산(&&)에는 두 값을 모두 평가하지 않아도 될 때가 있다. x && y가 있을 때, x의 값이 거짓 같은 값이면 y가 무엇이던간에 false이다. 마찬가지로 OR 연산(||)에서도 x || y가 있을 때, x가 참 같은 값이면 y가 무엇이던간에 true이다.
자바스크립트는 이런 방식으로 동작하며 이런 동작을 단축 평가
라고 한다.
단축 평가
가 중요한 이유는 두 번째 피연산자에 어떠한 것이 들어가도 단축 평가를 거치게 되면 그 효과는 일어나지 않기 때문이다.
const skipIt = true;
let x = 0;
const result = skipIt || x++; // result에는 true가 저장되고 x의 값은 그대로 0이다.
단축 평가는 아래와 같은 규칙을 따르게 된다.
x | y | x && y | x ll y |
---|---|---|---|
거짓 같은 값 | 거짓 같은 값 | x | y |
거짓 같은 값 | 참 같은 값 | x | y |
참 같은 값 | 거짓 같은 값 | y | x |
참 같은 값 | 참 같은 값 | y | x |
쉼표 연산자
는 표현식을 결합하여 왼쪽에서 오른쪽 순서로 평가한 후, 두 번째 표현식의 결과를 반환한다. 표현식을 하나 이상 실행하지만 마지막 표현식의 결과값만을 필요로 할 때 유용하다.
let x = 0, y = 10, z;
z = (x++, y++); // 10
typeof
연산자는 피연산자의 타입을 나타내는 문자열을 반환한다.
그런데 typeof null은 'object'를 반환한다. null은 객체가 아니라 원시 값이기 때문에 null을 반환해야 하지만 버그이고, 이미 typeof null이 'object'를 반환한다는 사실을 이용하는 코드가 너무 많이 생겨 수정할 수 없게 되었다.
null의 타입 체크는 일치 연산자를 사용하는게 좋다.
const a = null;
a === null; // true
ES6에서 새로 도입한 해체 할당
은 객체나 배열을 변수로 해체할 수 있다.
객체를 해체할 때는 반드시 변수 이름과 객체의 프로퍼티 이름이 일치해야하며, 프로퍼티 이름이 유효한 식별자인 프로퍼티만 해체 후 할당된다.
const obj = {b: 2, c: 3, d: 4}; // 객체 선언
const {a,b,c} = obj; // 해체 할당
a; // undefined
b; // 2
c; // 3
d; // ReferenceError
위 코드에서 obj에 a 프로퍼티가 없으므로 a에는 undefined가 할당된다. 해체 할당에는 d라는 프로퍼티가 없으므로 d는 선언조차 되지 않는다.
그리고 위 코드에서는 객체 해체의 선언과 할당을 같은 문에서 실행했다. 객체 해체는 할당만으로 이뤄질 수도 있지만 그렇게 하려면 아래 코드와 같이 반드시 괄호를 써야 한다.
const obj = {b: 2, c: 3, d: 4}; // 객체 선언
let a, b, c;
❌
{a, b, c} = obj;
✔
({a, b, c}) = obj;
배열을 해체할 때는 배열 요소에 대응할 변수 이름을 마음대로 쓸 수 있으며 배열 순서에 맞게 대응한다.
const arr = [1, 2, 3]; // 배열 선언
let [x, y] = arr; // 배열 해체 할당
x; // 1
y; // 2
z; // ReferenceError
위 코드에서 x는 배열의 첫 번째 요소 값을, y는 두 번째 요소 값을 할당받고 그 뒤의 배열 요소는 모두 버려진다.
확산 구문(...)
을 사용하면 남은 요소를 새 배열에 할당할 수 있다.
const arr = [1, 2, 3, 4, 5]; // 배열 선언
let [x, y, ...rest] = arr; // 배열 해체 할당
x; // 1
y; // 2
rest; // [3, 4, 5]
변수의 값을 서로 바꿀 때 임시 변수가 필수적이지만, 배열 해체를 활용하면 임시 변수 없이 변수의 값을 바꿀 수 있다.
let a = 5, b = 10;
[a, b] = [b, a];
a; // 10
b; // 5
if else 문의 목적이 변수의 값을 얻는 것이라면 코드가 간단해지고 읽기 쉬운 3항 연산자를 쓰는 편이 좋다.
if(isPrime(n)) {
label = 'prime';
} else {
label = 'non-prime';
}
👇
label = isPrime(n) ? 'prime' : 'non-prime';
할당이 주 목적인 if문은 단축 평가를 사용하는 OR 표현식을 써서 간결하게 줄일 수 있다.
if(!option) option = {};
👇
option = option || {};