Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
381 changes: 381 additions & 0 deletions JAVASCRIPT/남은열.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,381 @@
### 🌳 목차
- [실행컨텍스트](#실행컨텍스트)<br />
- 실행컨텍스트란?
- 렉시컬 환경의 대표적인 구성 요소
- 식별자 결정이란?
- 스코프 체인의 동작 원리
- 소스코드의 평과와 실행 과정
- 렉시컬 환경 vs 렉시컬 스코프
- 렉시컬 스코프와 동적 스코프의 차이는?

- [Closure](#closure)
- 자바스크립트에서의 일반적인 클로져란?
- 클로저의 장점과 단점

- [변수](#변수)
- 변수 호이스팅
- var, let, const
- TDZ(Temporal Dead Zone)이란?

- [this](#this)
- this란?
- 바인딩이란?
- call, apply, bind의 차이
- 아래에 출력되는 값은 무엇인가요? (번외)
- 문제가 있다면 왜 문제가 발생하고, 해결하는 방법은 무엇인가요? (번외)
- this 바인딩과 렉시컬 스코프의 차이점은?
- this 바인딩 우선순위

- [자료형](#자료형)
- 원시 타입
- 객체 타입
- 데이터 타입을 나누는 이유는 무엇인가?

- [연산자](#연산자)
- 산술 연산자
- 비교 연산자
- 아래 연산의 결과는 무엇인가?

---

<div id="실행컨텍스트" />

## 📌 실행컨텍스트

### 실행컨텍스트란?

실행컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고, 코드의 실행 결과를 실제로 관리하는 영역이다.

JavaScript의 실행 컨텍스트(Execution Context)는 JavaScript 엔진에서 구문을 해석하고 실행하는 동안 활용되는 메모리 공간입니다. 실행 컨텍스트는 스코프와 함께 각각의 함수, 블록, 전역 코드에 대한 환경을 구성하고, 엔진에서 각각의 구문을 실행할 수 있도록 합니다.

실행 컨텍스트는 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 매커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.

식별자와 스코프는 실행 컨테스트의 렉시컬 환경으로 관리하고 코드 실행 순서는 실행 컨텍스트 스택으로 관리한다.

JavaScript 엔진은 최초에 하나의 전역 실행 컨텍스트를 생성하고, 함수를 호출할 때마다 새로운 실행 컨텍스트가 생성됩니다. 각 실행 컨텍스트는 할당된 변수와 함수 객체를 포함하여, 엔진에서 언제든지 접근할 수 있도록 합니다. 실행이 끝난 후에는 실행 컨텍스트는 제거되며, 메모리에서 제거됩니다.

### 렉시컬 환경의 대표적인 구성 요소

1. 환경 레코드 (Environment Record)

스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소. 환경 레코드는 소스코드의 타입에 따라 관리하는 내용이 차이가 있다.

2. 외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)

외부 렉시컬 환경에 대한 참조는 상위 스코프를 가리킨다. 이때 상위 스코프란 외부 렉시컬 환경, 즉 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 의미. 외부 렉시컬 환경에 대한 참조를 통해 단방향 링크드 리스트인 스코프 체인을 구현한다.

### 식별자 결정이란?

동일한 이름의 식별자가 다른 스코프에 여러 개 존재할 수도 있다. 따라서 어느 스코프의 식별자를 참조하면 되는지 결정할 필요가 있는데, 이것을 식별자 결정이라 한다. 식별자 결정을 위해 식별자를 검색할 때는 실행 중인 실행 컨텍스트에서 식별자를 검색하기 시작한다. 만약 실행 중인 실행 컨텍스트의 렉시컬 환경에서 식별자를 검색할 수 없으면 외부 렉시컬 환경에 대한 참조가 가리키는 렉시컬 환경, 즉 상위 스포크로 이동하여 식별자를 검색한다.

### 스코프 체인의 동작 원리

현재 실행 중인 실행 컨텍스트의 렉시컬 환경에서 식별자를 검색할 수 없으면 외부 렉시컬 환경에 대한 참조가 가리키는 렉시컬 환경, 즉 상위 스코프로 이동하여 식별자를 검색하는 것.

### 소스코드의 평과와 실행 과정

소스코드 평가 과정에서는 실행 컨텍스트를 생성하고 변수, 함수 등의 선언문만 먼저 실행하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록한다. 소스코드 평가 과정이 끝나면 비로소 선언문을 제외한 소스코드가 순차적으로 실행되기 시작한다. 이때 소스코드 실행에 필요한 정보, 즉 변수나 함수의 참조를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득한다. 그리고 변수 값의 변경 등 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록한다.

### 렉시컬 환경 vs 렉시컬 스코프

렉시컬 환경은 코드가 실행될 때, 어떤 변수와 함수가 정의되어 있는지를 기억하는 객체이다. 렉시컬 환경은 언제든지 코드에서 접근 가능한 변수와 함수의 목록을 제공한다.

렉시컬 스코프는 렉시컬 환경에서 변수와 함수에 대한 접근 권한을 정의한다. 스코프는 변수와 함수에 접근할 수 있는 영역을 결정하며, 스코프는 변수와 함수의 유효 범위를 결정한다. 자바스크립트에서 스코프는 함수를 어디서 ‘선언’ 하였는지에 따라 결정된다.

즉, 렉시컬 환경은 변수와 함수의 목록을 제공하고, 렉시컬 스코프는 어떤 변수와 함수에 접근할 수 있는지를 결정한다. 두 개의 개념은 서로 연관되어 있지만, 다른 큰 개념을 가지고 있다.

이를 구현한 것이 “실행 컨테스트”이며, 코든 코드는 실행 컨텍스트에서 평가되고 실행된다.

### 렉시컬 스코프와 동적 스코프의 차이는?

상위 스코프를 결정하는 방법으로 함수를 어디서 ‘호출’ 하였는가와 함수를 어디서 ‘선언’하는 가 두 가지 패턴이 존재하는데, 첫 번째 방식을 동적 스코프, 두 번째 방식을 렉시컬 스코프라고 한다.

<div id="closure" />

## 📌 Closure

### Closure란?

JavaScript의 Closure는 함수와 그 함수가 선언된 스코프 사이에서 생성된 특수한 관계를 말합니다. Closure는 함수가 정의될 때 그 스코프에 있는 모든 변수와 함수에 대한 접근 권한을 갖는 함수를 생성합니다.

즉, Closure는 함수가 선언된 스코프에서 정의된 변수에 액세스 할 수 있는 함수를 의미합니다. Closure는 스코프 체인에서 정의된 변수를 "기억"하고, 그 변수를 가지고 있는 함수가 언제든지 사용 가능하게 합니다.

Closures를 사용하면 스코프에서 정의된 변수를 숨길 수 있고(정보은닉), 새로운 공간을 만들어 변수와 함수의 접근을 제한(캡슐화)할 수 있습니다. Closure는 자주 다른 객체와 함께 사용되어 객체의 내부적인 상태를 유지하는데 사용됩니다.

### 자바스크립트에서의 일반적인 클로져란?

중첩 함수의 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것

### 클로저의 장점과 단점

장점: 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.

단점: 메모리를 소모한다. outer와 inner 함수의 경우 outer 함수가 종료되었음에도 inner 함수가 종료될 때까지 메모리에서 해지되지 않고 있기 때문.

<div id="변수" />

## 📌 변수

### 변수 호이스팅

변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅이라한다. 자바스크립트 엔진은 변수 선언이 소스코드의 어디에 있든 상관없이 다른 코드보다 먼저 실행한다. 소스코드의 평가 과정에서 자바스크립트 엔진은 변수 선언을 포함한 모든 선언문을 소스코드에서 찾아내 먼저 실행한다. 따라서 변수 선언이 소스코드의 어디에 위치하는지와 상관없이 어디서든지 변수를 참조할 수 있다.

사실 변수 선언뿐 아니라 var, let, const, function, function\*, class 키워드를 사용해서 선언하는 모든 식별자는 호이스팅된다. 모든 선언문은 런타임 이전 단계에서 먼저 실행되기 때문이다.

### var, let, const

var, let const는 JavaScript의 변수를 선언하는 키워드이다. 세 키워드의 차이점은 다음과 같다.

1. var
- ES6 이전 버전에서 사용되던 변수 선언 방식
- 함수 스코프를 가진다.
- 변수 재선언과 재할당이 가능하다.
- 변수 호이스팅이 발생한다.
2. let
- ES6에서 도입되어 블록 스코프를 가진다.
- 변수 재선언이 불가능하다.
- 호이스팅이 발생하지 않는 것 처럼 동작한다.
- 재할당이 가능하다.
3. const
- ES6에서 도입된 상수 선언 방식이다.
- 블록 스코프를 가진다.
- 변수 값이 변하지 않는다.
- 변수 재선언이 불가능하다.
- 호이스팅이 발생하지 않는 것 처럼 동작한다.

### TDZ(Temporal Dead Zone)이란?

TDZ는 let, const 키워드를 사용하여 변수를 선언하였을 때, 해당 변수가 선언되기 전에 접근하려고 할 때 발생하는 현상이다.

TDZ는 변수가 선언된 위치에서부터 변수가 초기화될 때까지의 구간을 의미한다. 즉, 변수가 선언되었지만 초기화되기 전까지는 변수를 사용할 수 없다. 이 구간에서 변수에 접근하면 “ReferenceError” 오류가 발생한다.

TDZ는 let, const 키워드를 사용하는 변수에만 적용되며, var 키워드를 사용하는 변수는 소스코드 평가 단계에서 초기화가 진행되어 undefined가 할당되기 때문에 오류가 발생하지 않는다.

TDZ는 변수의 스코프를 명확하게 하기 위해 도입된 개념으로, 변수가 선언되지 않은 상태에서 사용될 경우 에러를 발생시켜 프로그램의 안정성을 높이는 역할을 한다.

<div id="this" />

## 📌 This

### this란?

JavaScript에서 this는 현재 실행 컨텍스트의 주체를 나타내는 예약어이다. 함수나 메소드 내부에서 this 키워드를 사용하면, 해당 함수를 호출한 객체에 대한 참조를 가르킨다. this는 실행 컨텍스트에 따라 참조하는 객체가 달라지기 때문에, this의 값은 동적으로 결정된다.

일반적으로, 함수를 전역 스코프에서 호출하면 this는 전역 객체를 가리킨다. 하지만, 함수를 객체의 메소드로 호출하면, this는 해당 객체를 가리킨다. 또한, 함수를 생성자로 호출하면 this는 새로 생성되는 객체를 가리킨다. 그리고 call, apply, bind 메서드를 통해 첫 번째 인자에 this를 직접 할당해줄 수 있다.

ES6에서는 화살표 함수를 사용하면, this가 함수를 정의한 스코프의 this와 같아지기 때문에, this 바인딩의 동작이 일반 함수와 다르다. 화살표 함수에서 this를 사용하면, 해당 함수를 정의한 스코프의 this 값을 참조하게 된다.

### 바인딩이란?

식별자와 값을 연결하는 과정을 의미한다. 예를 들어, 변수 선언은 변수 이름과 확보된 메모리 공간의 주소를 바인딩하는 것이다. this 바인딩은 this(키워드로 분류되지만 식별자 역할을 한다)와 this가 가리킬 객체를 바인딩하는 것이다.

### call, apply, bind의 차이

call, apply, bind는 자바스크립트에서 함수의 실행 컨텍스트를 변경하는 방법을 제공하는 메소드이다. 함수를 호출할 때, this의 값을 지정할 수 있다.

1. call

call 메소드의 첫 번째 인자는 함수 내부에서 this로 참조할 객체를 의미하며, 두 번째 이후의 인자들은 함수의 매개변수로 전달된다.

2. apply

call과 달리 두 번째 인자로 배열을 받으며, 이 배열은 함수의 매개변수로 전달된다.

3. bind

함수를 호출할 때 this의 값을 영구적으로 지정할 수 있다. bind 메소드는 새로운 함수를 반환하며, 이 함수를 호출하면 항상 지정한 this 값을 가지게 된다.

### 아래에 출력되는 값은 무엇인가요?

```jsx
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
},
};

setTimeout(user.sayHi, 1000);
```

### 문제가 있다면 왜 문제가 발생하고, 해결하는 방법은 무엇인가요?

setTimeout에 객체에서 분리된 함수인 user.sayHi가 전달되기 때문이다.

콜백함수로 넘긴 함수의 this에는 window가 할당이 되므로, window 객체에는 firstName이라는 값이 없기 때문에 `undefined`가 나타난다.

이를 해결하기 위한 방법

1. 래퍼 함수 사용

```jsx
setTimeout(function () {
user.sayHi(); // Hello, John!
}, 1000);
```

외부 렉시컬 환경에서 user를 받아서 보통 때처럼 메서드를 호출했기 때문

```jsx
setTimeout(() => user.sayHi(), 1000); // 동일
```

하지만 user가 변경이 되면, 변경된 객체의 메서드를 호출하게 되는 문제가 발생

```jsx
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
},
};

setTimeout(() => user.sayHi(), 1000);

// 1초가 지나기 전에 user의 값이 바뀜
user = {
sayHi() {
alert("또 다른 사용자!");
},
};

// setTimeout에 또 다른 사용자!
```

2. bind

```jsx
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
},
};

let sayHi = user.sayHi.bind(user); // (*)

// 이제 객체 없이도 객체 메서드를 호출할 수 있습니다.
sayHi(); // Hello, John!

setTimeout(sayHi, 1000); // Hello, John!

// 1초 이내에 user 값이 변화해도
// sayHi는 기존 값을 사용합니다.
user = {
sayHi() {
alert("또 다른 사용자!");
},
};
```

### this 바인딩과 렉시컬 스코프의 차이점은?

렉시컬 스코프와 this 바인딩은 결정 시기가 다르다. 함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정한다. 하지만 this 바인딩은 함수 호출 시기에 결정된다.

### this 바인딩 우선순위

JavaScript에서 `this`는 함수가 실행될 때 결정되는 값으로, 함수가 호출되는 방식에 따라 `this`가 참조하는 객체가 결정됩니다. 이때 `this`의 바인딩 우선순위는 다음과 같습니다.

1. new 바인딩
- 생성자 함수를 사용하여 새로운 객체를 생성할 때 `this`는 새로 생성된 객체를 참조합니다.
2. 명시적 바인딩 (Explicit binding)
- **`call`**, **`apply`**, **`bind`** 메서드를 사용하여 `this`를 명시적으로 지정할 수 있습니다.
3. 암시적 바인딩 (Implicit binding)
- 객체의 메서드를 호출할 때, 해당 메서드가 속해있는 객체가 `this`를 참조합니다.
4. 기본 바인딩 (Default binding)
- 전역 실행 컨텍스트에서 `this`는 전역 객체를 참조하며, strict mode에서는 `undefined`를 반환합니다.

만약 위 조건에 맞는 바인딩이 없는 경우, `this`는 undefined가 됩니다.

**`new`** 바인딩, 명시적 바인딩, 암시적 바인딩, 기본 바인딩은 모두 우선순위를 가지며, 바인딩이 겹치는 경우 우선순위가 높은 바인딩이 적용됩니다. 예를 들어, **`call`** 또는 **`apply`** 메서드를 사용해 명시적으로 `this`를 지정한 경우, 암시적 바인딩보다 우선순위가 높습니다. 하지만 `new` 바인딩이 적용된 경우, 모든 바인딩보다 우선순위가 가장 높습니다.

따라서, `this`의 값이 어떻게 결정되는지 이해하고 우선순위를 파악하여 적절한 바인딩 방식을 선택하는 것이 중요합니다.

<div id="자료형" />

## 📌 자료형

JavaScript의 자료형은 크게 원시 타입과 객체 타입 두 가지로 나뉜다

### 원시 타입

원시 타입은 값을 직접 저장하고 참조하는 타입으로, 다음과 같은 6가지 타입이 있다.

- number: 숫자
- string: 문자열
- boolean: 논리값 (참, 거짓)
- null: 값이 없음을 나타내는 특별한 타입
- undefined: 값이 할당되지 않음을 나타내는 특별한 타입
- symbol: 유일하고 변경 불가능한 값

원시 타입은 변수에 직접 값을 할당하므로, 변수끼리의 연산에서 값 자체가 비교된다. 이를 값 비교(value comparison)라고 한다.

### 객체 타입

객체 타입은 여러 개의 값을 하나의 변수에 저장하는 타입이다. 객체는 다양한 데이터 타입을 포함할 수 있으며, 객체 타입의 변수는 객체의 참조값(reference)을 저장한다. 객체 타입에는 다음과 같은 타입이 있.

- object: 일반적인 객체
- array: 배열
- function: 함수
- date: 날짜
- 등

객체 타입은 참조값을 저장하므로, 변수끼리의 연산에서 참조값이 비교된다. 이를 참조 비교(reference comparison)라고 한다.

JavaScript의 자료형은 동적으로 결정된다. 즉, 변수에 할당되는 값에 따라 자동으로 타입이 결정된다. 이러한 특징 때문에 JavaScript는 자유로운 타입 변환(free-type conversion)을 지원하며, 이를 이용해 다양한 연산을 수행할 수 있다.

### 데이터 타입을 나누는 이유는 무엇인가?

1. 값을 저장할 때 확보해야하는 메모리 공간의 크기를 결정하기 위해
2. 값을 참조할 때 한 번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해
3. 메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해

<div id="연산자" />

## 📌 연산자

### 산술 연산자

피연산자를 대상으로 수학적 계산을 수행해 새로운 숫자 값을 만든다. 산술 연산이 불가능한 경우, NaN을 반환한다.

1. 이항 산술 연산자

언제나 새로운 값을 만들 뿐

2. 단항 산술 연산자

증가/감소(++/—) 연산자는 피연산자의 값을 변경하는 부수 효과 발생, 피연산자의 값을 변경하는 암묵적 할당

숫자 타입이 아닌 피연산자에 + 단항 연산자를 사용하면 피연산자를 숫자 타입으로 변환하여 반환한다. 피연산자를 변경하는 것은 아니고 숫자 타입으로 변환한 값을 생성해서 반환한다.

3. 문자열 연결 연산자

- 연산자는 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작한다.

암묵적 타입 변환 또는 타입 강제 변환 발생

### 비교 연산자

- 동등/일치 비교 연산자
- 동등 비교 연산자: 느슨한 비교
- 일치 비교 연산자: 엄격한 비교
동등 비교 연산자는 좌항과 우항의 피연산자를 비교할 때 먼저 암묵적 타입 변환을 통해 타입을 일치시킨 후 같은 값인지 비교한다.

### 아래 연산의 결과는 무엇인가?

```jsx
// 동등 비교. 결과를 예측하기 어렵다.
"0" == ""; //  false
0 == ""; //  true
0 == "0"; //true
false == "false"; // false
false == "0"; // true
false == null; // false
false == undefined; //  false
```

```jsx
NaN === NaN; // false
0 === -0; // true
0 == -0; //true
```