# 모던 자바스크립트 Deep Dive 2

## Modern JavaScript Deep Dive 11\~20

## 11장 원시 값과 객체의 비교

### 원시 타입(값)

* **변경 불가능한immutable 값, 즉 읽기전용**
* **변경 불가능하다는 것은 변수가 아니라 값에 대한 진술**
* 상수(const)는 재할당이 금지된 변수일 뿐, 변경 불가능한 값과 동일시 하면 안됨
* 이러한 원시 값의 특성은 데이터의 신뢰성을 보장(불변성)
* **불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법이 없음**

#### 문자열

* **자바스크립트의 문자열(String 객체) 또한 원시 타입이며 변경 불가능함** (문자열이 생성된 이후에는 변경할 수 없음을 의미)
* 문자열은 **유사 배열 객체이면서 이터러블**이므로 배열과 유사하게 각 문자에 접근 가능
  * 유사 배열 객체(array-like object) : 배열처럼 인덱스로 프로펕 ㅣ값에 접근할 수 있고 length 프로퍼티를 갖는 객체

#### 값에 의한 전달

* 변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수에는 할당되는 변수의 원시 값이 복사되어 전달 => **값에 의한 전달**
* 하지만 할당받는 변수와 할당되는 변수의 값은 **다른 메모리 공간에 저장된 별개의 값**
* 따라서 값을 전달해주는 변수의 값을 변경해도 복사한 변수의 값에는 어떠한 영향도 없음
* **엄격하게 표현하면 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달되기 때문에** 값에 의한 전달이라는 용어는 오해가 있을 수 있음(변수와 같은 식별자는 값이 아니라 메모리 주소를 기억하기 때문)
* 단, 전달된 메모리 주소를 통해 메모리 공간에 접근하면 값을 참조할 수 있음
* **결국은 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되어 어느 한쪽에서 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없음**

### 객체

* 자바스크립트 객체의 관리 방식 : 자바스크립트 객체는 **프로퍼티 키를 인덱스로 사용하는 해시 테이블**이라고 생각할 수 있음, 또한 V8 자바스크립트 엔진에서는 프로퍼티에 접근하기 위해 **동적탐색 대신 히든 클래스라는 방식을 사용해 성능을 보장**

#### 변경 가능한 값

* **객체(참조)타입의 값, 즉 객체는 변경 가능한mutable 값**
* 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 **참조reference 값(생성된 객체가 저장된 메모리 공간의 주소, 그 자체)** 에 접근
* 그리고 메모리에 저장되어 있는 **참조 값을 통해 실제 객체에 접근**
* 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있음
* **객체의 부작용 : 여러 개의 식별자가 하나의 객체를 공유할 수 있음(참조에 의한 전달)**

**얕은shallow 복사와 깊은deep 복사**

* 객체를 프로퍼티 값으로 갖는 객체의 경우
* 얕은 복사 : 한 단계까지만 복사하는 것
* 깊은 복사 : 객체에 중접되어 있는 객체까지 모두 복사하는 것
* **얕은 복사와 깊은 복사로 생성된 객체는 원본과는 다른 객체, 즉 참조 값이 다른 별개의 객체**
* 참고로 원시 값을 할당한 변수를 다른 변수에 할당하는 것을 깊은 복사, 객체를 하랑한 변수를 다른 변수에 할당하는 것을 얕은 복사라고 부르는 경우도 있음

#### 참조에 의한 전달

* 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 **참조 값이 복사되어 전달** => **참조에 의한 전달**
* 두개의 변수, 두개의 식별자가 하나의 객체를 공유
* 결국 **값에 의한 전달과 참조에 의한 전달은 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일**
* 따라서 자바스크립트에는 **참조에 의한 전달은 존재하지 않고 값에 의한 전달만이 존재한다고 말할 수 있음(포인터 개념이 없기때문에)**
* **전달되는 값의 종류가 원시 값인지 참조 값인지 구별**해서 강조하는 의미에서 값에 의한 전달/참조에 의한 전달로 구분

## 12장 함수

### 함수란?

* 자바스크립트에서 가장 중요한 핵심 개념
* **일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것**
* 함수 정의를 통해 생성하고 인자와 함께 함수 호출을 통해 반환값을 반환

### 함수를 사용하는 이유

* 코드의 재사용
* 유지보수의 편의성
* 코드의 신뢰성
* 코드의 가독성

### 함수 리터럴

* 함수는 객체 타입의 값으로 함수 리터럴로 생성 **(함수 === 객체)**
* 함수 리터럴 : function 키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성
* **일반 객체는 호출할 수 없지만 함수는 호출할 수 있음**

### 함수 정의

* 함수를 호출하기 이전에 인수에 전달받을 매개변수와 실행할 문들, 그리고 반환할 값을 지정하는 것
* 정의된 함수는 엔진에 의해 평가되어 함수 객체가 됨

  #### 함수 정의하는 방법

  * **함수 선언문** : function add(x, y) { return x+y; }
    * 이름생략 불가, 표현식X 문O
    * **자바스크립트 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당**
    * 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출
  * **함수 표현식** : var add = function(x, y) { return x+y; };
    * 자바스크립트 함수는 일급 객체
    * 함수 리터럴로 생성한 함수 객체를 변수에 할당할 수 있음
    * 일반적으로 이름 생략, 표현식O 문O

      > 함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점은 다름
  * Function 생성자 함수 : var add = new Function('x', 'y', 'return x + y');
    * new 연산자 없이 호출해도 동일
    * 이러한 방식으로 생성된 함수는 클로저를 생성하지 않는 등 다르게 동작함
  * 화살표 함수(ES6) : var add = (x, y) => x + y;
    * 표현만 간략할 뿐만 아니라 내부 동작 또한 간략화됨(26장에서 설명)
* 변수는 선언, 함수는 정의
* **함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 함수 호이스팅이라고함**
* **함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생(표현식 이전에 참조하면 undefined를 반환)**

### 함수 호출

* 함수는 함수를 가리키는 식별자와 한 쌍의 소괄호인 함수 호출 연산자로 호출

  #### 매개변수와 인수

  * 매개변수는 함수를 정의할 때 선언하며 함수 내부에서 변수와 동일하게 취급
  * 함수는 매개변수의 개수와 인수의 개수가 일치하는지 체크하지 않음
  * 따라서 **인수가 부족해서 인수가 할당되지 않은 매개변수 값은 undefined, 초과된 값은 무시**

  #### 인수 확인

  * 자바스크립트는 동적 타이핑 언어로 매개변수의 타입을 사전에 지정할 수 없음
  * 따라서 **함수를 정의할 때 적절한 인수가 전달되었는지 확인할 필요가 있음**

  #### 매개변수의 최대 개수

  * **이상적인 함수는 한 가지 일만 해야 하며 가급적 작게 만들어야함**
  * 따라서 **매개변수는 최대 3개 이상을 넘지 않는 것을 권장**
  * 다수의 매개변수가 필요하다면 객체로 인수를 전달하는 것이 유리
    * 주의할 것은 함수 외부에서 내부로 전달한 객체를 함수 내부에서 변경하면 외부의 객체가 변경되는 부수 효과가 발생

  #### 반환문

  * 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나감
  * 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환(명시적으로 지정하지 않으면 undefined가 반환)

### 참조에 의한 전달과 외부 상태의 변경

* 매개변수 또한 타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따름
* **객체타입 인수는 참조 값이 복사되어 매개변수에 전달 되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손됨**

### 다양한 함수의 형태

* **즉시 실행 함수(IIFE)**

  * 함수 정의와 동시에 즉시 호출되는 함수로 단 한 번만 호출됨
  * 익명 함수를 사용하는 것이 일반적
  * 즉시 실행 함수는 반드시 그룹 연산자 (...)로 감싸야 함 `(function () { ... }());`

  ```
  // 즉시 실행 함수도 일반 함수처럼 인수를 전달할 수 있음
  res = (function (a, b) {
    return a * b;
  }(3, 5));
  ```
* **재귀 함수**
  * 자기 자신을 호출하는 행위, 즉 재귀 호출을 수행하는 함수를 말함
  * 재귀 함수는 자신을 무한 재귀 호출 함, 따라서 재귀 호출을 멈출 수 있는 **탈출 조건**을 만드시 만들어야함(스택 오버플로 에러 발생)
  * 특수한 상황에 한정적으로 사용하는 것이 바람직
* **중첩 함수**
  * 함수 내부에 정의된 함수를 **중첩 함수 도는 내부 함수** 라고 함
  * 중첩 함수를 포함하는 함수는 **외부 함수**
  * 일반적으로 중첩 함수는 **자신을 포함하는 외부함수를 돕는 헬퍼 함수의 역할을 함**
* **콜백 함수**
  * 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 **콜백 함수**라고함
  * 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 **고차 함수(HOF)** 라고 함
  * 즉, **고차 함수는 콜백 함수를 자신의 일부분으로 합성함**
  * 고차 함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출
  * 콜백 함수는 고차 함수에 의해 호출되며 이때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달
  * **콜백 함수는 함수형 프로그래밍 패러다임뿐만 아니라 비동기 처리(이벤트, ajax, 타이머 함수 등)에 활용되는 중요한 패턴**
* **순수 함수와 비순수 함수**
  * 순수 함수 : 어떤 외부 상태에 의존하지도 변경하지도 않는, 부수 효과가 없는 함수
  * 비순수 함수 : 부수 효과가 있는 함수
  * 함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화해서 불변성을 지향하는 프로그래밍 패러다임

## 13장 스코프

* 스코프(유효범위)는 자바스크립트를 포함한 모든 프로그래밍 언어의 기본적이며 중요한 개념
* var 키워드로 선언한 변수와 let 또는 const 키워드로 선언한 변수의 스코프는 다르게 동작
  * var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언이 허용
* **모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정 됨 => 스코프 === 식별자가 유효한 범위**
* **식별자를 검색(결정)할 때 사용하는 규칙 => 스코프**
* 스코프 내에서 식별자는 유일해야 하지만 다른 스코프에는 같은 이름의 식별자를 사용할 수 있음 즉, **스코프는 네임스페이스**

### 렉시컬lexical 환경

* 코드가 어디서 실행되며 주변에 어떤 코드가 있는지
* 즉, 코드의 문맥context는 렉시컬 환경으로 이루어짐 => 이를 구현한 것이 **실행 컨텍스트**(23장)

### 스코프의 종류

* 전역global : 코드의 가장 바깥 영역, 전역 스코프, 전역 변수
  * 전역 변수는 어디서든지 참조할 수 있음
* 지역local : 함수 몸체 내부, 지역 스코프, 지역 변수
  * 지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서 유효

### 스코프 체인

* 함수는 중첩될 수 있으므로 함수의 지역 스코프도 중첩될 수 있음 => **스코프가 함수의 중첩에 의해 계층적 구조를 갖는다는 것을 의미**
* **스코프 체인 === 모든 스코프는 하나의 계층적 구조로 연결 되며 모든 지역스코프의 최상위 스코프는 전역 스코프**
* 변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색함
  * 따라서 상위 스코프에서 선언한 변수를 하위 스코프에서도 참조할 수 있음
  * 스코프 체인으로 연결된 스코프의 계층적 구조는 **부자 관계로 이뤄진 상속과 유사함**(부모의 자산을 자식이 사용할 수 있지만 반대는 불가능)

### 함수 레벨 스코프

* **함수 레벨 스코프 => 코드 블록이 아닌 함수에 의해서만 지역 스코프가 생성됨**
  * var 키워드로 선언된 변수는오로지 함수의 코드 블록만을 지역 스코프로 인정(함수 레벨 스코프)
* 블록 레벨 스코프 => 모든 코드 블록이 지역 스코프를 만듬
  * ES6에서 도입된 **let과 const 키워드는 블록 레벨 스코프를 지원**

### 렉시컬 스코프

* **렉시컬 스코프(정적 스코프) <=> 동적 스코프**
* **함수를 어디서 정의했는지에 따라 상위 스코프를 결정,** 호출된 위치는 스코프 결정에 어떠한 영향도 주지 않음

## 14장 전역 변수의 문제점

### 변수의 생명 주기

* **지역 변수의 생명 주기**
  * 함수 내부에서 선언된 지역 변수는 함수가 호출되면 생성되고 함수가 종료하면 소멸
  * 변수 호이스팅은 엄밀히 말해서 전역변수에 한정된 것
  * **지역 변수의 생명 주기는 함수의 생명 주기와 일치**
  * 스코프는 누군가 참조하고 있다면 해제되지 않고 생존함(24장 클로저)
  * **호이스팅은 스코프 단위로 동작 => 지역 변수의 호이스팅은 지역 변수의 선언이 지역 스코프의 선두로 끌어 올려진 것처럼 동작**
  * 즉, **호이스팅은 변수 선언이 스코프의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징**
* **전역 변수의 생명 주기**
  * 전역 객체 : 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 어떤 객체보다 먼저 생성되는 특수한 객체로 CS환경에서는 window, SS환경에서는 global객체를 의미
  * **var 키워드로 선언한 전역 변수의 생명주기는 전역 객체의 생명주기와 일치**

### 전역 변수의 문제점

* **암묵적 결합** : 모든 코드가 전역 변수를 참조하고 변경할 수 있는 암묵적 결합을 허용하는 것
* **긴 생명주기** : 전역 변수는 생명 주기가 길어 메모리 리소스도 오랜 기간 소비하고 상태 변경에 의한 오류 발생확률이 높음
* **스코프 체인 상에서 종점에 존재** : 전역 변수의 검색 속도가 가장 느림
* **네임스페이스 오염** : 자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도 하나의 전역 스코프를 공유한다는 것. 따라서 다른 파일 내에서 동일한 이름으로 명명될 경우 예상치 못한 결과를 가져 올수 있음

### 전역 변수 사용을 억제하는 방법

* 전역 변수를 반드시 사용해야할 이유를 찾지 못한다면 지역 변수를 사용
* 변수의 스코프는 좁을수록 좋음
* **즉시 실행 함수** : 모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 됨
* **모듈 패턴** : 클래스를 모방해서 관련 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈로 만듬. 클로저를 기반으로 동작하며 전역 변수의 억제는 물론 캡슐화까지 구현할 수 있음
* **ES6 모듈** : **ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공**, var 키워드로 선언해도 더는 전역 변수도 window객체의 프로퍼티도 아님(모던 브라우저에서 사용)
  * `<script type="module" src="lib.mjs"></script>`

## 15장 let, const 키워드와 블록 레벨 스코프

### var 키워드로 선언한 변수의 문제점

* 변수 중복 선언 허용
  * 초기화문이 있는 변수 선언문은 엔진에 의해 var 키워드가 없는 것처럼 동작(이전에 선언한 변수 값이 변경)
* 함수 레벨 스코프
  * 함수 외부에서 var 키워드로 선언한 변수는 코드 블록 내에서 선언해도 모두 전역 변수
* 변수 호이스팅
  * var 키워드로 선언한 변수는 변수 선언문 이전에 참조할 수 있음 단, 언제나 undefined를 반환
  * 에러를 발생시키지는 않지만 프로그램 흐름상 맞지 않을 뿐더러 가독성을 떨어뜨리고 오류를 발생할 여지를 남김

### let 키워드

* **변수 중복 선언 금지**
  * let 키워드로 이름이 같은 변수를 중복 선언하면 SyntaxError가 발생
* **블록 레벨 스코프**
  * 모든 코드 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 따름 **(함수도 코드 블록이므로 스코프를 만듬)**
* **변수 호이스팅**
  * let 키워드로 선언한 변수는 "선언"과 "초기화"가 분리되어 진행, 따라서 선언문 이전에 참조하면 ReferenceError가 발생
  * **일시적 사각지대(TDZ)** : 스코프 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간
* **전역 객체와 let**
  * let 키워드로 선언한 전역 변수는 전역 객체(window)의 프로퍼티가 아님(보이지 않는 개념적인 블록 내에 존재)

### const 키워드

* const 키워드는 상수를 선언하기 위해 사용(대부분 let 키워드와 동일, 아래는 let과의 차이점)
* **선언과 초기화**
  * const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 함(SyntaxError)
* **재할당 금지**
  * const 키워드로 선언한 변수는 재할당이 금지됨(TypeError)
* **상수**
  * 재할당이 금지된 변수를 말함
  * 상수는 상태 유지와 가독성, 유지보수의 편의를 위해 적극적으로 사용해야 함
  * const 키워드로 선언된 변수에 원시 값을 할당한 경우 원시 값은 변경할 수 없는 값이고 const 키워드에 의해 재할당이 금지되므로 할당된 값을 변경할 수 있는 방법은 없음
  * 일반적으로 상수의 이름은 대문자로 선언해 상수임을 명확히 나타냄(스테이크 케이스)
* **const 키워드와 객체**
  * const 키워드로 선언된 변수에 객체를 할당한 경우 값을 변경할 수 있음
  * 변경 가능한 값인 객체는 재할당 없이도 직접 변경이 가능
  * const 키워드는 재할당을 금지할 뿐 "불변"을 의미하지 않음

### var vs. let vs. const

* ES6를 사용한다면 var 키워드는 사용하지 않음
* 재할당이 필요한 경우에 한정해 let 키워드를 사용(이때 변수 스코프는 최대한 좁게)
* 변경이 발생하지 않고 읽기 전용으로 사용하는 원시 값과 객체에는 const 키워드를 사용
* 변수를 선언할 때는 일단 const 키워드를 사용하고 재할당이 필요하다면 let으로 변경

## 16장 프로퍼티 어트리뷰트

### 내부 슬롯과 내부 메서드

* 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드
* 자바스크립트 엔진의 내부 로직이므로 원칙적으로 자바스크립트는 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않음
* 단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공
  * ex. 모든 객체는 \[\[Prototype]]이라는 내부 슬롯을 가짐. `__proto__`를 통해 간접적으로 접근 가능

### 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체

* **자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동정의**
* 프로퍼티의 상태 => 프로퍼티의 값, 값의 갱신 가능 여부, 열거 가능 여부, 재정의 가능 여부
* 프로퍼티 어트리뷰트 => 자바스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯 `[[Value]], [[Writeable]], [[Enumerable]], [[Configurable]]`
  * 직접 접근할 수는 없지만 `Object.getOwnPropertyDescriptor()` 메서드를 사용하여 간접적으로 확인할 수 있음
  * \=> **하나의 프로퍼티에 대해 프로퍼티 디스크립터 객체를 반환**
  * `Object.getOwnPropertyDescriptors()` ES8에서 도입된 메서드로 모든 프로퍼티에 대해 프로퍼티 디스크립터 객체를 반환

### 데이터 프로퍼티와 접근자 프로퍼티

* 데이터 프로퍼티 : 키와 값으로 구성된 일반적인 프로퍼티
  * 프로퍼티 어트리뷰트를 가짐 `[[Value]], [[Writeable]], [[Enumerable]], [[Configurable]]`
* 접근자 프로퍼티 : 자체적으로는 값을 갖지 않고 데이터 프로퍼티의 값을 읽거나 저장될 때 호출되는 접근자 함수로 구성된 프로퍼티
  * `[[Get]], [[Set]], [[Enumerable]], [[Configurable]]`
  * getter와 setter 함수를 모두 정의할 수도 있고 하나만 정의할 수도 있음

    ```
    get fullName() {
      return `${this.firstName} ${this.lastName}`
    }
    set fullName(name) {
      [this.firstName, this.lastName] = name.split(' ');
    }
    ```
* 프로토타입 : 어떤 객체의 상위(부모) 객체의 역할을 하는 객체(19장)

### 프로퍼티 정의

* 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것
* `Objcet.defineProperty()` 메서드를 사용하면 프로퍼티의 어트리뷰트를 정의할 수 있음
* 생략했을 때의 기본 값 : `undefined` `false`
* `Object.defineProperties()` 메서드를 사용하면 여러 개의 프로퍼티를 한번에 정의할 수 있음

### 객체 변경 방지

* 객체는 변경 가능한 값이므로 재할당 없이 직접 변경할 수 있음
* 자바스크립트는 객체의 변경을 방지하는 다양한 메서드를 제공
* **객체 확장 금지**
  * `Object.preventExtensions()`
  * 확장이 금지된 객체는 프로퍼티 추가만 금지됨
  * `Object.isExtensible()` 확장 가능 객체 여부 확인
* **객체 밀봉**
  * `Object.seal()`
  * 밀봉된 객체는 읽기와 쓰기만 가능(추가,삭제,재정의 금지)
  * `Object.isSealed()` 밀봉 여부 확인
* **객체 동결**
  * `Object.freeze()`
  * 동결된 객체는 읽기만 가능
  * `Object.isFrozen()` 동결 여부 확인
* 위와 같은 변경 방지 메서드들은 얕은 변경 방지로 직속 프로퍼티만 변경이 방지되고 중첩 객체까지는 영향을 주지 못함
* **불변 객체**
  * 객체의 중첩 객체까지 동결하여 변경이 불가능한 읽기 전용 객체
  * 구현하기 위해서 객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로 `Object.freeze()`를 호출해야함

## 17장 생성자 함수에 의한 객체 생성

### Object 생성자 함수

* `new Object()` : 빈객체를 생성하여 반환
* 생성자 함수(constructor)란 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수
* Object 이외에도 `String` `Number` `Boolean` `Function` `Array` `Date` `RegExp` `Promise` 등의 빌트인 생성자 함수를 제공

### 생성자 함수

* **객체 리터럴에 의한 객체 생성 방식의 문제점**
  * 직관적이고 간편하지만 단 하나의 객체만 생성하므로 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적
* **생성자 함수에 의한 객체 생성 방식의 장점**
  * 객체를 생성하기 위한 템플릿(클래스)처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있음
    * **this** : 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수(self-referencing variable), `this` 바인딩은 함수 호출 방식에 따라 동적으로 결정(22장)
  * 형식이 정해져 있는 것이 아니라 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 `new 연산자`와 함께 호출하면 해당 함수는 생성자 함수로 동작
* **생성자 함수의 인스턴스 생성 과정**
  * 생성자 함수의 함수 몸체에서 수행해야 하는 것 : 인스턴스 생성, 생성된 인스턴스 초기화(프로퍼티 추가 및 초기화 할당)
  * 1. 인스턴스 생성과 this 바인딩 : 빈 객체를 생성하고 this에 바인딩 시킴
  * 1. 인스턴스 초기화 : 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스를 초기화
  * 1. 인스턴스 반환 : 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환
* **내부 메서드 \[\[Call]]과 \[\[Construct]]**
  * 함수는 객체이므로 일반 객체와 동일하게 new 연산자와 함께 호출하여 객체를 생성할 수 있음(생성자 함수로서 호출)
  * 함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드를 모두 가지고 있음
  * 일반 객체는 호출할 수 없지만 함수 객체는 호출할 수 있음(함수로 동작하기 위한 내부 슬롯 + 내부 메서드)
  * 일반 함수로서 호출되면 \[\[Call]]이 호출되고 생성자 함수로서 호출되면 \[\[Construct]]가 호출
  * callable : 호출할 수 있는 객체 === 모든 함수 객체
  * constructor : 생성자 함수로서 호출할 수 있는 함수
  * non-constructor : 생성자 함수로서 호출할 수 없는 함수
* **constructor와 non-construcotr의 구분**
  * 자바스크립트 엔진은 함수 정의를 평가하여 함수 객체를 생성할 때 함수 정의 방식에 따라 구분함
  * constructor : 함수 선언문, 함수표현식, 클래스(클래스도 함수)
  * non-constructor : 메서드(ES6 메서드 축약 표현만), 화살표 함수
* **new 연산자**
  * new 연산자와 호출하는 함수는 constructor 이어야 함(\[\[Construct]]호출)
  * new 연산자와 함께 생성자 함수로서 호출하면 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킴(그렇지 않으면 전역 객체 window를 가리킴)
  * 일반 함수와 생성자 함수에 특별한 형식적 차이가 없으므로 생성자 함수는 파스칼 케이스로 명명하여 구별할 수 있도록 노력
* **new\.target**
  * 생성자 함수가 new 연산자 없이 호출되는 위험성을 회피하기 위해 ES6에서는 new\.target을 지원
  * new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new\.target은 함수 자신을 가리키고 new 연산자 없이 힐반 함수로서 호출된 함수 내부의 new\.target은 undefined
  * 함수 내부에서 new\.target을 사용하여 생성자 함수로서 호출했는지 확인하여 그렇지 않은 경우 new 연산자와 함께 재귀 호출을 통해 생성자 함수로서 호출할 수 있음

## 18장 함수와 일급 객체

### 일급 객체

* 무명의 리터럴로 생성할 수 있다. 즉 런타임에 생성이 가능하다.
* 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
* 함수의 매개변수에 전달할 수 있다.
* 함수의 반환값으로 사용할 수 있다.
* 함수는 객체이지만 일반 객체와는 다르게 호출할 수 있고 함수 고유의 프로퍼티를 소유함

### 함수 객체의 프로퍼티

* 함수는 객체이기 때문에 프로퍼티를 가질 수 있음 `console.dir()`
  * **arguments 프로퍼티**
    * 함수 객체의 auguments 프로퍼티 값은 arguments 객체
    * arguments 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회 가능한 **유사 배열 객체**이며 함수 내부에서 지역변수 처럼 사용
    * 함수가 호출되면 함수 몸체 내에서 암묵적으로 매개변수가 선언되고 undefined로 초기화된 이후 인수가 할당
    * 자바 스크립트는 함수의 매개변수와 인수의 개수가 일치하는지 확인하지 않음
    * 인수가 전달되지 않은 매개변수는 undefined, 초과된 인수는 무시되고 arguments 객체 프로퍼티로 보관
    * arguments 객체는 매개변수 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 유용
  * **caller 프로퍼티**
    * ECMAScript 사양에 포함되지 않은 비표준 프로퍼티
    * 함수 자신을 호출한 함수를 가리킴
  * **length 프로퍼티**
    * 함수를 정의할 때 선언한 매개변수의 개수를 가리킴
    * arguments는 인자 / length는 매개변수
  * **name 프로퍼티**
    * 함수 이름을 나타내는 프로퍼티로 ES6에서 정식 표준이 됨
    * 익명 함수 표현식의 경우 ES5에서는 빈 문자열을, ES6에서는 함수 객체를 가리키는 식별자를 값으로 가짐
  * **\_\_proto\_\_ 접근자 프로퍼티**
    * 모든 객체는 \[\[Prototype]]이라는 내부 슬롯을 가지며 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티임
    * 간접적으로 프로토타입 객체에 접근하기 위한 접근자
  * **prototype 프로퍼티**
    * 생성자 함수로 호출할 수 있는 함수 객체, 즉 constructor만이 소유하는 프로퍼티로 non-constructor에는 prototype 프로퍼티가 없음
    * 함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킴

## 19장 프로토타입

* 자바스크립트는 **명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원**하는 멀티 패러다임 프로그래밍 언어(imperative, functional, prototype-based OOP)
* ES6에서 클래스가 도입되었으나 기존의 프로토타입 기반 객체지향 모델을 대체하는 것은 아님
* 자바스크립트는 객체 기반의 프로그래밍 언어이며 **자바스크립트를 이루고 있는 거의 모든 것이 객체**

### 객체지향 프로그래밍

* 프로그램을 명령어 또는 함수의 목록으로 보는 전통적인 명령형 프로그래밍의 절차지향적 관점에서 벗어나 여러 개의 독립적 단위, 즉 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 말함
* **추상화** : 다양한 속성 중에서 프로그램에 필요한 속성만 간추려 내어 표현하는 것
* **객체** : 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조, 즉 **상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조**

### 상속과 프로토타입

* **상속** : 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
* **자바스크립트는 프로토타입을 기반으로 상속을 구현함**

```
function Circle(radius) {
  this.radius = radius;
}

Circle.protorype.getArea = function() {
  return Math.PI * this.radius ** 2;
}
// Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유
```

### 프로토타입 객체

* 객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고 \[\[prototype]]에 저장됨
* 모든 객체는 하나의 프로토타입을 가지며 모든 프로토타입은 생성자 함수와 연결되어 있음
* **\_\_proto\_\_ 접근자 프로퍼티** : 모든 객체는 \_\_proto\_\_ 접근자 프로퍼티를 통해 자신의 프로토타입, 즉 \[\[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있음
  * **\_\_proto\_\_ 는 접근자 프로퍼티다**
  * **\_\_proto\_\_ 접근자 프로퍼티는 상속을 통해 사용된다** : 객체가 직접 소유 하는 프로퍼티가 아님
  * **\_\_proto\_\_ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유** : 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서(순환 참조), 아무런 체크 없이 무조건적으로 프로토타입을 교체할 수 없도록 함
  * **\_\_proto\_\_ 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다** : 모든 객체가 \_\_proto\_\_ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문, 접근자 프로퍼티 대신 프로토타입의 참조를 취득하고 싶은 경우 Object.getPrototypeOf 메서드를 사용하고 프로토타입을 교체하고 싶은 경우 Object.setPrototypeOf 메서드를 사용할 것을 권장
* **함수 객체의 prototype 프로퍼티**
  * 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킴
  * non-constructor인 화살표 함수와 ES6 메서드 축약 표현으로 정의한 메서드는 prototype 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않음
  * **모든 객체가 가지고 있는(Object.prototype으로부터 상속받은) \_\_proto\_\_ 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토 타입을 가리킴, 다만 사용하는 주체가 다름(모든 객체 or 생성자 함수)**
* **프로토타입의 constructor 프로퍼티와 생성자 함수**
  * 모든 프로토타입은 constructor 프로퍼티를 가지며 이 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킴
  * 이 연결은 생성자 함수가 생성될 때, 즉 함수 객체가 생성될 때 이루어짐

### 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

* 리터럴 표기법에 의해 생성된 객체도 물론 프로토타입이 존재하지만 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정할 수는 없음
* ECMAScript 사양에 따르면 Object 생성자 함수에 **인수를 전달하지 않거나 undefined 또는 null을 인수로 전달하면서 호출하면** 내부적으로는 추상 연산 OrdinaryObjectCreate를 호출하여 **Object.prototype을 프로토타입으로 갖는 빈 객체를 생성**
* 객체 리터럴에 의해 생성된 객체는 Object 생성자 함수가 생성한 객체가 아님
* **프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재**
* 큰 틀에서 생각해보면 리터럴 표기법으로 생성한 객체도 생성자 함수로 생성한 객체와 본질적인 면에서 큰 차이는 없음

### 프로토타입의 생성 시점

* 모든 객체는 생성자 함수와 연결되어 있고 **프로토타입은 생성자 함수가 생성되는 시점에서 더불어 생성**

#### 사용자 정의 생성자 함수와 프로토타입 생성 시점

* 생성자 함수로서 호출할 수 있는 함수, 즉 constructor는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성
* non-constructor는 프로토타입이 생성되지 않음
* **사용자 정의 생성자 함수는 자신이 평가되어 함수 객체로 생성되는 시점에 프로토타입도 더불어 생성되며, 생성된 프로토타입의 프로토타입은 언제나 Object.prototype**

#### 빌트인 생성자 함수와 프로토타입 생성 시점

* 빌트인 생성자 함수도 일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성
* 생성자 함수와 프로토타입은 이미 객체화 되어 존재, **이후 생성자 함수 또는 리터럴 표기법으로 객체를 생성하면 프로토타입은 생성된 객체의 \[\[Prototype]] 내부 슬롯에 할당**

### 객체 생성 방식과 프로토타입의 결정

* 다양한 방식(객체 리터럴, Object 생성자 함수, 생성자 함수)으로 생성된 모든 객체는 각 방식마다 세부적인 객체 생성 방식의 차이는 있으나 **추상 연산 OrdinaryObjectCreate에 의해 생성된다는 공통점이 있음**
* 즉, 프로토타입은 추상 연산 OrdinaryObjectCreate에 전달되는 인수에 의해 결정
* 사용자 정의 생성자 함수에 의해 생성된 객체의 프로토타입
  * 생성자 함수에 의해 생성되는 객체의 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩 되어 있는 객체
  * 사용자 정의 생성자 함수와 더불어 생성된 프로토타입 생성자함수.prototype의 프로퍼티는 constructor뿐임
  * 프로토타입은 객체이기 때문에 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있음 그리고 이렇게 추가/삭제된 프로퍼티는 프로토타입 체인에 즉각 반영

### 프로토타입 체인

* 프로토타입의 프로토타입은 언제나 Object.prototype
* **자바스크립트는 객체의 프로퍼티(메서드 포함)에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 \[\[Prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색하고 이를 프로토타입 체인이라 함**
* **프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 매커니즘**
* `Object.prototype` : 프로토타입 체인의 종점(end of prototype chain)
* **프로토타입 체인은 상속과 프로퍼티 검색을 위한 매커니즘**
* 자바스크립트 엔진은 함수의 중첩 관계로 이루어진 스코프의 계층적 구조에서 식별자를 검색, 따라서 **스코프 체인은 식별자 검색을 위한 매커니즘**
* **스코프 체인과 프로토타입 체인은 서로 연관없이 별도로 동작하는 것이 아니라 서로 협력하여 식별자와 프로퍼티를 검색하는 데 사용**

### 오버라이딩과 프로퍼티 섀도잉

* `프로퍼티 섀도잉` : 상속 관계에 의해 프로퍼티가 가려지는 현상
* 프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 프로토타입 체인을 따라 프로토타입 프로퍼티를 검색하여 덮어쓰는 것이 아니라 인스턴스 프로퍼티로 추가함. 이때 인스턴스 메서드는 프로토타입 메서드를 `오버라이딩` 했음

### 프로토타입의 교체

* 프로토타입은 임의의 다른 객체로 변경할 수 있음 === 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미
* 프로토타입은 `생성자 함수` 또는 `인스턴스` 에 의해 교체할 수 있음(294p)
* **프로토타입 교체를 통해 객체 간의 상속 관계를 동적으로 변경하는 것은 번거로우므로 직접 교체하지 않는 것이 좋음**

### instanceof 연산자

* `객체 instanceof 생성자 함수`
* **우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true로 평가되고 그렇지 않은 경우 false로 평가**
* 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인 상에 존재하는지 확인

### 직접 상속

#### Object.create에 의한 직접 상속

* `Object.create` 메서드는 명시적으로 프로토타입을 지정하여 새로운 객체를 생성(OrdinaryObjectCreate 호출)
* new 연산자가 없이도 객체를 생성할 수 있음
* 프로토타입을 지정하면서 객체를 생성할 수 있음
* 객체 리터럴에 의해 생성된 객체도 상속받을 수 있음

#### 객체 리터럴 내부에서 \_\_proto\_\_ 에 의한 직접 상속

* ES6에서는 객체 리터럴 내부에서 \_\_proto\_\_ 접근자 프로퍼티를 사용하여 직접 상속을 구현

### 정적 프로퍼티/메서드

* 생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드를 말함
* 정적 프로퍼티/메서드는 인스턴스의 프로토타입 체인에 속한 객체의 프로퍼티/메서드가 아니므로 인스턴스로 접근할 수 없음
* 프로토타입 프로퍼티/메서드를 표기할 때 prototype을 #으로 표기하는 경우도 있음

### 프로퍼티 존재 확인

1. in 연산자 `key in object`
   * in 연산자는 확인 대상 객체의 프로퍼티뿐만 아니라 확인 대상 객체가 상속받는 모든 프로토타입의 프로퍼티를 확인하므로 주의가 필요
2. in 연산자 대신 ES6 도입 `Reflect.has` 사용
3. `Object.prototype.hasOwnProperty` 메서드
   * 인수로 전달받은 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 true를 반환하고 상속받은 프로토타입의 프로퍼티 키인 경우 false를 반환

### 프로퍼티 열거

1. `for ... in` 문
   * 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어트리뷰트 \[\[Enumerable]]의 값이 true인 프로퍼티를 순회하며 열거함
   * 객체 자신의 고유 프로퍼티 뿐만 아니라 상속받은 프로퍼티도 열거하므로 `Object.prototype.hasOwnProperty` 메서드를 사용하여 객체 자신의 프로퍼티인지 확인하는 추가 처리가 필요
   * 배열에서는 일반적인 `for` 문이나 `for ... of` 문 또는 `Array.prototype.forEach` 메서드를 사용하기를 권장
2. `Object.keys/values/entries` 메서드
   * 객체 자신의 고유 프로퍼티만 열거하기 위해 권장되는 메서드들
   * `keys` : 객체 자신의 열거가능한 프로퍼티 키를 배열로 반환
   * `values` : 객체 자신의 열거가능한 프로퍼티 값을 배열로 반환
   * `entries` : 객체 자신의 열거가능한 프로퍼티 키와 값의 쌍의 배열을 베열에 담아 반환
