Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- TDZ
- function barrowing
- partial application
- 배낭 문제
- 함수 빌려쓰기
- 백준
- 결손치 제거
- utm parameter
- 사용자 속성
- 개방 시스템 상호 연결
- jupyter
- script tag
- seaborn
- 동적 계획법
- GA4
- 결측치 제거
- git workflow
- 우선순위 큐
- dinamic programming
- ES6
- Priority Queue
- pandas
- 렉시컬 스코프
- jupyter notebook
- javascript
- Java
- Git Action
- jupyter extension
- yarn berry
- nextjs
Archives
- Today
- Total
FoO의 개발 블로그
[JS 개념정리 3] scope, 스코프 체인, 호이스팅, TDZ, 클로저 본문
JavaScript의 scope(스코프)는 변수와 함수의 접근성과 생존 기간을 결정하는 규칙이다.
스코프의 종류
- 전역 스코프(Global Scope)
- 함수 스코프(Function Scope)
- 블록 스코프(Block Scope)
- 렉시컬 스코프(Lexical Scope)
스코프와 관련된 개념들
- 스코프 체인(Scope Chain)
- 호이스팅(Hoisting)
- 클로저(Closure)
전역 스코프(Global Scope)
- 코드의 가장 바깥쪽에 선언된 변수나 함수
- 어디서든 접근 가능
- 과도한 사용은 네임스페이스 오염을 일으킬 수 있음
함수 스코프(Function Scope)
- 함수 내부에 선언된 변수
- var 키워드로 선언된 변수에 적용
- 해당 함수 내에서만 접근 가능
function exampleFunction() {
var functionScopedVar = "I'm function-scoped";
console.log(functionScopedVar); // 정상 작동
}
console.log(functionScopedVar); // ReferenceError
- let과 const로 선언된 변수도 함수 스코프의 영향을 받지만 var와는 다른 특성을 가짐
- 블록 스코프를 가짐
- 함수 스코프도 적용(함수도 하나의 블록이기 때문)
- var와 다르게 블록 레벨 스코프를 존중
- 예를 들어, 함수안의 블럭(if문 등)에서 var로 변수가 선언된 경우 해당 함수 내의 어디서든 해당 변수에 접근이 가능하지만, let과 const로 선언된 변수는 블럭을 벗어나면 접근할 수 없게 됨.
function blockScopeExample() {
if (true) {
var varVariable = "I'm var";
let letVariable = "I'm let";
const constVariable = "I'm const";
}
console.log(varVariable); // "I'm var" (함수 스코프)
console.log(letVariable); // ReferenceError (블록 스코프)
console.log(constVariable); // ReferenceError (블록 스코프)
}
blockScopeExample();
블록 스코프(Block Scope)
- ES6에서 도입된 let과 const 키워드로 선언된 변수에 적용
- 가장 가까운 중괄호 {} 내에서만 접근 가능
if (true) {
let blockScopedVar = "I'm block-scoped";
const alsoBlockScoped = "Me too";
console.log(blockScopedVar); // 정상 작동
}
console.log(blockScopedVar); // ReferenceError
렉시컬 스코프(Lexical Scope)
- 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정
- 클로저(Closure)와 밀접한 관련이 있음
function outer() {
var outerVar = "I'm from outer";
function inner() {
console.log(outerVar); // 'outer' 함수의 변수에 접근 가능
}
inner();
}
outer();
스코프 체인(Scope Chain)
- 내부 스코프에서 외부 스코프로 변수를 찾아가는 과정
- 가장 가까운 스코프부터 검색하여 전역 스코프까지 올라감
var globalVar = "global";
function outer() {
var outerVar = "outer";
function inner() {
var innerVar = "inner";
console.log(innerVar, outerVar, globalVar);
}
inner();
}
outer(); // "inner outer global"
호이스팅(Hoisting)
- 변수와 함수 선언이 스코프의 최상단으로 끌어올려지는 것처럼 동작
- var로 선언된 변수와 함수 선언문에 적용
- let과 const는 블록 스코프를 가지며 호이스팅 되지 않음(TDZ(Temporal Dead Zone)으로 인해 초기화 전 접근 불가)
console.log(hoistedVar); // undefined (에러가 발생하지 않음)
var hoistedVar = "I'm hoisted";
hoistedFunction(); // 정상 작동
function hoistedFunction() {
console.log("I'm a hoisted function");
}
TDZ(Temporal Dead Zone)
ECMAScript 6(ES6)에서 let과 const 키워드와 함께 도입된 개념이다. 변수가 선언된 위치부터 초기화되기 전까지의 코드 영역을 가리킨다.
- 변수 접근 제한
- 변수가 선언되기 전에 접근하는 것을 방지
- var의 호이스팅으로 인한 혼란을 줄이고 코드의 예측 가능성을 높이기 위함
- 적용 대상
- let과 const로 선언된 변수에 적용
- 클래스 선언에도 적용
- 동작 방식
- 변수가 스코프에 들어가는 시점부터 선언되는 시점까지 TDZ에 있다고 봄
- 이 기간 동안 변수에 접근하려고 하면 ReferenceError 발생
{
// TDZ 시작
console.log(x); // ReferenceError
let x = 5; // TDZ 종료, x 초기화
console.log(x); // 5 (정상 작동)
}
{
// TDZ 시작
const func = () => console.log(x);
// 여기서 func를 호출하면 에러 발생
let x = 5; // TDZ 종료, x 초기화
func(); // 5 (정상 작동)
}
TDZ의 이점
- 버그 예방: 변수를 선언하기 전에 사용하는 실수를 방지
- 코드 명확성: 변수가 어디서 선언되었는지 명확히 알 수 있음
- const의 의미 강화: const로 선언된 변수가 항상 초기값을 가지도록 보장
클로저(Closure)
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합이다.
다르게 말하면, 함수가 자신이 선언된 스코프의 변수들에 접근할 수 있는 메커니즘이다.
기본 개념
- 내부 함수가 외부 함수의 변수에 접근할 수 있다.
- 외부 함수가 반환된 후에도 내부 함수는 외부 함수의 변수를 계속 참조할 수 있다.
function outerFunction(x) {
let y = 10;
function innerFunction() {
console.log(x + y);
}
return innerFunction;
}
const closure = outerFunction(5);
closure(); // 출력: 15
데이터 프라이버시
- 클로저를 사용해 private 변수를 흉내낼 수 있다.
function createCounter() {
let count = 0;
return {
increment: function() { count++; },
decrement: function() { count--; },
getCount: function() { return count; }
};
}
const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 출력: 2
console.log(count); // ReferenceError: count is not defined
위 예에서 createCounter 함수가 종료 됐음에도 내부 함수에서 여전히 createCounter 스코프에 있는 count 변수에 접근할 수 있다. 내부 함수를 제외하곤 count 변수에 접근할 수 있는 방법은 없다.
함수 팩토리
- 클로저를 사용해 다양한 함수를 생성할 수 있다.
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
console.log(double(5)); // 출력: 10
console.log(triple(5)); // 출력: 15
외부 함수가 반환된 후에도 내부 함수는 외부 함수의 변수를 계속 참조할 수 있기 때문에 선언시 사용된 factor 변수를 계속하여 사용할 수 있다.
모듈 패턴
- 클로저를 사용해 private 멤버와 public 멤버를 가진 모듈을 만들 수 있다.
const myModule = (function() {
let privateVar = 0;
function privateFunction() {
console.log('private function');
}
return {
publicVar: 1,
publicFunction: function() {
privateVar++;
privateFunction();
console.log(privateVar);
}
};
})();
myModule.publicFunction(); // 출력: private function, 1
console.log(myModule.publicVar); // 출력: 1
console.log(myModule.privateVar); // undefined
이벤트 핸들러와 콜백
- 비동기 작업에서 클로저를 활용할 수 있다.
function setupButton(label) {
let count = 0;
const button = document.createElement('button');
button.textContent = label;
button.addEventListener('click', function() {
count++;
console.log(`${label} clicked ${count} times`);
});
document.body.appendChild(button);
}
setupButton('Button A');
setupButton('Button B');
클릭시 동작하는 콜백 함수에서 외부 함수의 변수인 label과 const에 계속해서 접근이 가능하다.
클로저 사용시 주의사항
- 메모리 사용: 클로저는 외부 변수를 참조하므로 메모리를 더 사용할 수 있다.
- 가비지 컬렉션: 클로저로 인해 참조된 변수는 가비지 컬렉션이 되지 않는다.
- 성능: 위 이유로 과도한 클로저 사용은 성능에 영향을 줄 수 있다.
'Programming > FE' 카테고리의 다른 글
[JS 개념정리 2] call, apply, bind 메서드 (1) | 2024.06.30 |
---|---|
[JS 개념정리 1] script 태그 속성, ES6 module, strict mode, 화살표함수 (0) | 2024.06.30 |
Next.js 프로젝트에 GA4 붙이기(사용자 속성, 잠재고객, UTM) (0) | 2024.06.16 |
[troubleshooting] yarn berry와 깃헙 액션 사용시 주의점 (Error: Cannot find module '.yarn/releases/yarn-3.6.4.cjs') (4) | 2023.10.13 |