자바스크립트 완벽 가이드 3장 정리
포스트
취소

자바스크립트 완벽 가이드 3장 정리

해당 포스트는 자바스크립트 완벽 가이드라는 교재로 스터디를 하면서 3장을 정리한 포스트입니다.
주관적으로 해석한 내용이 들어가 있어서 잘못된 내용이 포함될 수 있습니다.

📌 3장의 정리 및 핵심

  1. 자바스크립트에서 숫자와 문자열을 만들고 조작하는 방법
  2. 자바스크립트의 다른 기본 타입인 boolean, Symbol, null, undefined를 다루는 법
  3. 불변인 기본 타입과 가변인 참조 타입의 차이
  4. 자바스크립트가 묵시적으로 타입을 변환하는 과정, 그리고 프로그램에서 직접 변환하는 방법
  5. 상수와 변수를 선언하고 초기화하는 방법, 선언하는 변수와 상수의 어휘적 스코프

기본 타입은 어떤 방법으로도 변경이 불가능합니다.
변경된 것처럼 보인다면 그것은 변경이 아닌 새로운 값을 만든것입니다.

📌 타입

프로그래밍 언어에서 표현하고 조작할 수 있는 값의 종류

1. 기본 타입

기본 타입은 값이 불변입니다.

  1. number
  2. string
  3. boolean
  4. null
  5. undefined
  6. Symbol

2. 객체 타입

기본 타입을 제외한 모든 타입들은 객체 타입입니다.
객체 타입은 값이 가변입니다.

array, function, class, Set, Map, Date 등이 있습니다.

📌 리터럴

직접 기입한 값

1
2
3
4
5
6
// 아래에서 10과 20이 숫자 리터럴
// 유사한 형태로 문자열, 객체, 함수 등의 리터럴이 존재함
let number = 10;
number = 20;

const num = number;

📌 number 타입

1. 특별한 number 타입들

Infinite, -Infinite, NaN

  • Number.isNaN() vs isNaN()
1
2
3
4
5
isNaN("1"); // false
isNaN("1a"); // true

Number.isNaN("1"); // false
Number.isNaN("1a"); // false

2. 특이한 number 타입 비교

소수점을 정확하게 표현하지 못해서 비교는 하지 않는 것이 좋다.

1
2
3
4
const n1 = 0.3 - 0.2; // 0.09999999999999998
const n2 = 0.2 - 0.1; // 0.1

console.log(n1 === n2); // false

3. BigInt

천문학적으로 큰 수라면 BigInt를 사용하면 됩니다.

📌 string 타입

핵심은 문자열은 불변이라 변할 수 없습니다.
문자열이 배열과 같이 생겨서 배열처럼 변경할 수 있다고 느껴질 수 있습니다.
하지만 문자 타입은 기본 타입이라 불변한 값입니다.

1
2
3
let str = "apple";
str[0] = "b";
console.log(str); // "apple"

혹시 “문자 메서드로 문자를 변경하는 것이 아닌가?”라고 생각할 수 있지만 그것 또한 원본은 놔두고 복사본을 생성하는 것입니다.
결국 중요한 것은 “메서드로 처리한 값들은 결국 새로운 문자열을 만들어서 반환하는 것이다.”라는 점입니다.

1
2
3
let str = "apple";
str.replaceAll("p", "q");
console.log(str); // "apple"

📌 null vs undefined

null은 의도적으로 값이 없음을 의미
undefined는 초기화되지 않은 변수의 값 혹은 존재하지 않는 프로퍼티나 배열 요소에 접근한 경우 반환하는 값

typeof null === "object"는 초기 자바스크립트의 버그라고 합니다.

📌 Symbol

리터럴이 존재하지 않습니다.
객체의 키로 사용할 수 있는 값입니다.

Symbol은 객체의 키중에서 충돌하지 않는 유일한 값으로 사용하기 위해서 개발되었습니다.
이게 무슨말이냐면 ES6의 이터러블을 도입할 때 객체마다 겹치지 않는 유일한 이름의 key가 필요했습니다.
만약에 iterator이라는 키를 사용해버리면 기존에 존재했고 iterator이라는 키를 사용하는 객체는 키가 중복되어버려서 제대로 된 동작을 할 수 없습니다.
그렇기 때문에 Symbol이라는 타입을 만들고 객체의 key로 사용할 수 있으며, 기존의 어떤 값과도 중복될 수 없는 유니크한 값을 만들도록 개발했습니다.

📌 falsy한 값

boolean타입으로 변환하면 false가 되는 값들입니다.

  1. undefined
  2. null
  3. 0, -0
  4. NaN
  5. ""

빈 객체({}), 빈 배열([])은 포함되지 않습니다.

📌 전역 객체

  • 기본적으로 갖는 값들
    1. 전역 상수 ( null, NaN, Infinite )
    2. 전역 함수 ( isNaN(), parseInt() )
    3. 생성자 함수 ( Date(), Number(), Object() )
    4. 전역 객체 ( Math, JSON )

브라우저의 전역 객체는 window, 노드의 전역 객체는 global, 표준 globalThis입니다.

📌 객체 타입은 참조 값을 가진다.

배열과 객체는 참조 값을 갖기 때문에 같으려면 동일한 참조 값을 가져야 한다.

1
2
3
4
5
6
7
8
9
10
11
const arr1 = [];
const arr2 = [];

console.log(arr1 === arr2); // false

const arr3 = arr1;
arr3[0] = 10;

console.log(arr1[0]); // 10
console.log(arr3[0]); // 10
console.log(arr1 === arr3); // true

📌 명시적 타입 변환

1
2
3
4
5
6
7
8
9
10
11
// 문자로 변환 ( 값 + 빈 문자열 )
1 + ""; // "1"
// "+"는 문자열 연결 연산자, 더하기 연산자로 사용할 수 있기 때문에 문자열을 우선으로 생각합니다.
// 하지만 "-", "*", "/"의 경우 피연산자가 문자열이라면 자동으로 숫자로 변환되어 계산됩니다.

// 숫자로 변환 ( +문자 )
+"1.2";   // 1.2

// 불리언으로 변환 ( !!값 )
!!"1";  // true
!!0;  // false

📌 let과 const 선언 규칙

  1. 방법1) 모두 const로 선언 후 바뀌는 값에만 let 선언
  2. 방법2) 바뀌지 않는 값에만 const 선언

저도 책의 저자와 같이 1번 방식을 더 선호합니다.

📌 스코프

프로그램 소스 코드에서 해당 변수가 정의된 영역을 의미합니다.
let, const는 블록 스코프, var는 함수 스코프를 가집니다.

📌 선언과 타입

자바스크립트는 변수는 타입이 존재하지 않고 어떤 타입이든 넣을 수 있습니다.
좀 바꿔서 말하면 number타입을 갖는 변수에 string타입의 값을 넣어도 상관없다는 의미입니다.
하지만 이런 방식은 좋은 프로그래밍 스타일이 아니라서 지양하는게 좋습니다.

📌 가비지 컬렉션

JS는 접근할 수 없는 값을 알아서 메모리 할당 해제를 시켜줘서 프로그래머가 신경써주지 않아도 됩니다.
위 MDN 링크를 읽어보니 Mark-and-sweep 알고리즘을 이용하기 때문에 순환 참조를 해도 상관없다고 적혀있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 접근할 수 없는 값의 예시
let arr = [1, 2, 3, 4, 5];

// "arr"을 이용한 어떤 특별한 처리를 했고 더 이상 "arr"을 사용하지 않는다고 가정
const sum = arr.reduce((p, c) => p + c);

// 이제 "[1, 2, 3, 4, 5]"라는 값에 접근할 수 있는 방법은 어디에도 없음
// 따라서 가비지 컬렉터가 그것을 인지하고 본인이 원할 때 할당을 해제 시켜서 메모리의 낭비된 공간을 없앰
arr = null;

// 순환 참조 ( mdn 예시 )
// "f()"라는 함수가 끝나도 "x -> y, y -> x를 참조하기 때문에 x와 y에 접근할 수 없더라고 할당 해제 되지 않는다"
// 라고 알고 있었는데 "Mark-and-sweep"알고리즘을 사용하기 때문에 할당 해제 시킨다고 합니다.
function f() {
  var x = {};
  var y = {};
  x.a = y;         // x는 y를 참조합니다.
  y.a = x;         // y는 x를 참조합니다.

  return "azerty";
}

f();

📌 래퍼 객체

객체만이 메서드를 가질 수 있습니다.
하지만 기본 nullundefined를 제외한 기본 타입들도 메서드를 사용할 수 있습니다.

그 이유는 래퍼 객체 덕분입니다.
Number, String, Object같이 이미 존재하는 타입의 객체를 래퍼 객체라고 합니다.

1
2
3
4
5
6
7
8
9
// 아래와 같은 코드가 가능한 이유는 래퍼 객체 덕분입니다.
"apple".toLocaleUpperCase(); // "APPLE"

/**
 * 1. `new String("apple")` 으로 String 객체 생성
 * 2. String 객체가 가지는 `toLocaleUpperCase()`라는 메서드 사용 ( 콘솔에 `String.prototype`을 입력해보면 어떤 메서드들이 있는지 알 수 있다. )
 * 3. 객체는 버리고 기본 타입으로 변경 ( 객체가 기본 타입보단 더 무겁기 때문에 버림 )
 * ( 3-1. 버려진 객체는 누구도 접근할 수 없으므로 가비지 컬렉터에 의해서 메모리 할당 해제가 된다. )
 */

📌 작성하지 않는 주제

  1. Math
  2. 날짜와 시간 ( 11장에서 설명한다고 함 )
  3. 정규표현식 ( 11장에서 설명한다고 함 )
  4. 일치 연산자(==)와 동등 연산자( === )
  5. 분해 할당(destructuring)… 이거는 배열이랑 객체의 개념이 많이 필요해서 생략

❓ 의문

1. 호이스팅

책에서는 호이스팅을 var의 특징이고 let의 결점이라고 이해했는데 제가 공부했던 바로는 let/const처럼 오류가 나는 것이 더 합리적이라고 생각합니다.
코드는 위에서 아래, 좌측에서 우측으로 읽는데 선언을 하기전에 사용하면 오류가 나는게 조금 더 사람이 이해하기 쉽다고 생각합니다.
그래서 변수는 사용하기전에 선언하는 것이 좋고, 함수는 함수 표현식을 이용해서 선언해서 선언 전의 접근을 막는 것이 좋다고 생각합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// console.log(number);  // ReferenceError: number is not defined
// console.log(digit);   // ReferenceError: digit is not defined
func1()   // "func1()"
// func2()   // ReferenceError: func2 is not defined

const number = 10;
let digit = 20;

function func1() {
  console.log("func1()");
}
// 함수 표현식
const func2 = function() {
  console.log("func2()");
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

Intersection Observer API과 React.js로 무한 스크롤링 구현

Git과 GitHub에 대한 정리