<aside> 💡 코드를 보다 쉽게 이해, 저장, 수정할 수 있도록 메모리 주소에 이름을 부여하여 의미를 갖도록 만든 형태를 변수/함수라고 함.
</aside>
<aside> 💡 이름 충돌의 문제를 피하기 위해 스코프라는 규칙을 만들어 정의함.
</aside>
클로저에 대해 설명해주세요.
# 등장배경
- 클로저는 자바스크립트 언어 명세에 기재되어 있지 않음. 이 단어는 1964년 Peter J. Landin 의 논문 "The Mechanical Evaluation of Expressions" 에서 등장함.
- 위 정의를 토대로, 현대 프로그래밍 관점에서 해석하자면 이렇게 정의할 수 있음 : **함수 + 함수를 둘러싼 환경 (Lexical environment) = 클로저**
- 클로저는 함수가 생성되는 시점에 생성되며, 생성된 함수의 렉시컬 환경을 포섭한 하나의 개념임. 즉, 개념적인 측면에서 자바스크립트의 모든 함수는 클로저라고 볼 수 있음. 하지만 실제로 모든 자바스크립트 함수를 클로저라고 부르지는 않음!
function a() {
let a = 'a';
function b() {
console.log(a);
}
}
****# 예시
var color = 'red'; function foo() { var color = 'blue'; // 2 function bar() { console.log(color); // 1 } function setState(new) { color = new; } return setState; } var baz = foo(); // 3 setState('red'); // 4
- `bar`는 자신이 생성된 렉시컬 스코프에서 벗어나 글로벌 환경에서 `baz`라는 이름으로 호출이 됨.
- 실제 동작 결과, `color`를 탐색하는 과정에서 현재 실행 스택과 관련 없는 `foo`에서 값을 찾게 됨. 이는 `baz`를 `bar`로 초기화 하는 과정에서 렉시컬 환경을 `foo`로 결정했기 때문임.
- 일반적으로 `foo` 수행이 끝난 이후 메모리를 회수해야 하지만, `bar`가 여전히 렉시컬 환경인 `foo`를 참조하고 있기 때문에 소멸되지 않고 남아있게 됨.
=> 이처럼 이미 동작이 끝나 소멸되어야 하는 환경을 특정 환경이 참조하고 있는 경우 소멸되지 않고 남아있는 현상을 클로저라고 함.
클로저가 필요한 이유는 무엇일까요?
- 자바스크립트에서 함수가 일급 객체로 취급되기 때문에 필요하다고 생각함. 대표적으로 함수가 함수를 반환했을 때, 내부에 참조하고 있는 값이 소멸될 경우 함수 동작에 문제가 생김. 자바스크립트는 선언 방식에 따라 동작하기 때문에 클로저 개념이 없다면 참조 에러가 발생할 것이라 예상됨.
- 클로저를 활용하면 전역 변수를 사용하지 않고도 특정 상태를 기억하고 최신으로 유지할 수 있음. 더불어 이러한 상태는 외부에서 접근할 수 없도록 프라이빗하게 구현할 수 있기 때문에 정보를 은닉하거나 예상치 못한 변화에 따른 위험성을 줄이는 데 도움이 됨.
일급 객체란 무엇인가요?
변수에 할당, 반환값으로 사용, 함수의 파라미터로 전달, 리터럴 방식으로 생성이 가능한 객체를 일급 객체라고 함.
+) 리터럴 방식으로 함수를 정의하고 변수에 할당하는 방식을 함수 표현식이라고 함.
+) 자바스크립트에서 함수가 일급 객체로 취급되기 때문에 함수형 프로그래밍이 가능함. -> 함수형 패러다임을 갖춘 언어
어떤 상황에 클로저가 사용되나요?
클로저 개념이 존재하기 때문에 아래와 같은 동작 구현이 가능함.
# 전역 변수 사용 억제
- 전역 변수를 사용하지 않고도 현재 상태(값)를 기억하고 변경된 최신 상태를 유지할 수 있음.
<https://velog.io/@open_h/closure-and-scope>
# 변수 값 은닉
- 자바스크립트에는 자체적인 프라이빗 메서드가 존재하지 않지만, 클로저를 이용하면 구현할 수 있음.
- 프라이빗 메서드는 접근 불가 은닉이 핵심 -> 그 다음 구현 방식 이야기하기
# reack hooks (useState, useMemo)
- useState 는 클로저 기법으로 구현됨.
- 렉시컬 환경
<aside> ❓ 각 상황에 따른 예시 정리 필요
</aside>
스코프에 대해 설명해주세요.
# 스코프란?
- 식별자(변수)의 유효 범위: 어떤 변수가 정의되어 있는 영역을 뜻함. 식별자(변수)가 어디서 어떻게 선언되었는지에 따라 스코프가 결정됨.
- 자바스크립트는 기본적으로 렉시컬 스코프와 함수 레벨 스코프 방식으로 동작함. ES6부터는 블록 레벨 스코프를 지원함.
## 1-1. 전역 스코프 (global scope)
- 코드 내부 어디서든 참조(접근)가 가능한 영역을 뜻함.
## 1-2. 지역 스코프 (local scope)
- 코드 블록({...})이 만든 영역을 뜻함. 자기 자신과 하위 스코프에서만 참조(접근)가 가능함.
- 전역 변수와 지역 변수의 이름이 같을 경우, 전역 변수는 감춰지고 지역 변수를 사용함.
## 2-1. **함수 레벨 스코프 (function-level scope)**
- 함수를 기준으로 스코프를 결정: 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하며 외부에서는 참조(접근)할 수 없음. var 키워드로 선언된 변수가 이 개념에 해당함.
****- 대부분의 프로그래밍 언어는 블록 레벨 스코프를 따르는 반면, 자바스크립트는 함수 레벨 스코프를 따름. 모든 변수가 함수 내부에서 전역적으로 동작하다 보니 전역 변수가 일으키는 예상치 못한 동작, 변수 중복 선언 & 변수 선언 이전 참조 가능으로 인한 문제 발생과 같은 혼란을 야기함.
## 2-2. **블록 레벨 스코프 (block-level scope)**
- 함수 레벨 스코프의 단점을 보완하기 위해 ES6부터 블록 레벨 스코프를 지원하기 시작함.
****- 블록을 기준으로 스코프를 결정: 모든 코드 블록(함수, if, for, try/catch 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 외부에서는 참조(접근)할 수 없음. let/const 키워드로 선언된 변수가 이 개념에 해당함.
- 함수 레벨 스코프의 경우 함수 내부에 선언된 변수는 어디서든 접근이 가능했지만, 블록 레벨 스코프의 경우 변수가 포함된 스코프 내부가 아닌 곳에서 사용할 경우 참조 에러가 발생함.
## 3-1. **렉시컬 스코프 (Lexical scope)**
- 정적/어휘적/수사적 스코프라고도 함. 현대 프로그래밍에서 대부분의 언어들은 이 규칙을 따르고 있음.
- 소스코드가 작성된 문맥에서 결정되는 스코프를 뜻함. 즉, 함수를 어디서 호출했는지가 아닌 어디에 선언하였는지에 따라 정해짐.
****
## 3-2. **동적 스코프 (Dynamic scope)**
- 프로그램의 런타임 도중의 실행 컨텍스트나 호출 컨텍스트에 의해 결정되는 스코프를 뜻함. 즉, 렉시컬 스코프와는 반대로 함수를 어디서 호출했는지에 따라 정해짐.
스코프 체인에 대해 설명해주세요.
# 개념
- 자바스크립트 엔진은 식별자를 찾을 때 1) 먼저 자신이 속한 스코프에서 찾고, 2) 그 스코프에 식별자가 존재하지 않는 경우 상위 스코프(렉시컬 환경)에서 다시 찾게 됨. 3) 모든 스코프 안에서 식별자를 찾지 못하는 경우 (렉시컬 환경 참조가 `null`이 될 때) 참조 에러가 발생함.
- 위와 같이 체인처럼 꼬리에 꼬리를 물고 상위 스코프를 참조하는 현상을 스코프 체인이라고 함. 이는 중첩되어 있는 모든 상황에서 발생함.
# 동작방식
<https://meetup.toast.com/posts/86>
Lexical Environment와 Environment Record의 개념
<aside> ❓ 함수가 호출될 때마다 스코프 체인이 달라진다는데, 어떻게 동작하는 걸까? + 실행 컨텍스트와 엮어서 생각해보자.
</aside>
var
, let
, const
의 개념 및 차이점을 설명해주세요.
# var
- 동일한 이름으로 여러 번 중복해서 선언이 가능함. 이와 같은 경우, 마지막에 할당된 값이 변수에 저장됨.
- 필요할 때마다 변수를 유연하게 사용할 수 있다는 장점이 될 수도 있지만, 기존에 선언해둔 변수의 존재를 잊고 값을 재할당하는 등의 실수가 발생할 가능성이 큼. 특히 코드량이 많아졌을 때, 같은 이름의 변수명이 여러 번 선언되었다면 어디 부분에서 문제가 발생하는지 파악하기 힘들뿐더러 값이 바뀔 우려가 있음.
- 이를 보완하기 위해 ES6부터 추가된 변수 선언 방식이 `let`과 `const`임.
# let
- `var`와 다르게 `let`은 중복 선언할 경우 해당 변수가 이미 선언되었다는 에러 메시지가 출력됨. (중복 선언 불가능)
- `name = 'a'`와 같이 변수 선언 및 초기화 이후 반복해서 다른 값을 재할당 할 수는 있음.
# const
- `let`과 `const`의 차이점은 `immutable`의 여부임. `const`는 constant(상수)를 뜻하기 때문에 한 번만 선언이 가능하며 값을 바꿀 수도 없음. `let`은 변수에 다른 값을 재할당할 수 있지만, `const`는 재할당 시 에러 메시지가 출력됨.
- 단, 배열과 오브젝트의 값을 변경하는 것은 가능함. 불변을 의미하는 것과 다르게 값을 재할당하는 코드만 불가능하다고 볼 수 있음.
각 키워드는 어떻게 사용하는 것이 좋을까요?
- `var`의 역할은 `let/const`가 충분히 대체할 수 있기 때문에 사용하지 않는 것이 좋음.
- `const`를 기본으로 사용하여 불필요한 변수의 재사용을 방지하고, 재할당이 필요한 경우에만 `let`을 사용하는 것이 좋음.
함수 선언문
과 함수 표현식
의 차이점을 설명해주세요.
함수 생성에 필요
# 함수 선언문
`function foo() { ... }`
# 함수 표현식
`const = `
- 함수를 변수에 담은 것
function a () { if (true) { var b = function () { console.log('a') } // const b = function () { console.log('a') } b() } b() }
- 어떤 키워드를 사용하냐에 따라 스코프가 달라짐. `var`를 사용해서 선언하면 함수 레벨 스코프로 동작하고 `let/const`를 사용해서 선언하면 블록 레벨 스코프로 동작함.
<aside> ❓ 생성자도 함께 정리
</aside>