1. 렉시컬 스코프란 무엇인가?
렉시컬 스코프는 코드 작성 위치에 따라 변수와 함수의 유효 범위를 결정하는 방식입니다. 즉, 변수나 함수를 사용하려면 해당 코드가 선언된 위치를 고려해야 합니다.
렉시컬 스코프는 다음과 같은 특징을 가집니다.
- 정적 스코프라고도 불립니다. 컴파일 시 코드 분석을 통해 스코프 체인을 결정하기 때문에, 실행 과정에서 동적으로 변하지 않습니다.
- 블록 스코프와 함수 스코프를 포함합니다.
- let, const, function 키워드를 사용하여 변수와 함수를 선언할 때 적용됩니다.
2. 렉시컬 스코프 vs. 다이내믹 스코프
다이내믹 스코프는 변수나 함수를 사용하는 위치에 따라 유효 범위를 결정하는 방식입니다. 즉, 변수나 함수를 사용하는 코드가 실행되는 순간에 해당 변수나 함수의 유효 범위를 판단합니다.
렉시컬 스코프와 다이내믹 스코프의 주요 차이점은 다음과 같습니다.
결정 시점 | 컴파일 시 | 실행 시 |
특징 | 정적, 예측 가능 | 동적, 불확실 |
장점 | 코드 가독성 향상, 오류 감소 | 코드 재사용성 향상 |
단점 | 재귀 함수 구현 어려움 | 변수 오염 가능성 증가 |
3. 렉시컬 스코프 작동 방식
렉시컬 스코프는 다음과 같은 방식으로 작동합니다.
- 코드 분석: 컴파일러는 코드를 분석하여 변수와 함수의 선언 위치를 파악합니다.
- 스코프 체인 생성: 각 코드 블록마다 해당 블록에서 유효한 변수와 함수를 포함하는 스코프 체인을 생성합니다.
- 변수/함수 검색: 변수나 함수를 사용할 때, 먼저 현재 코드 블록의 스코프 체인에서 해당 변수나 함수를 검색합니다.
- 외부 블록 검색: 만약 현재 블록에서 찾지 못하면, 외부 블록의 스코프 체인을 차례대로 검색합니다.
- 전역 스코프 검색: 최종적으로 전역 스코프 체인에서 검색합니다.
4. 렉시컬 스코프 활용
렉시컬 스코프는 다음과 같은 상황에서 유용하게 활용됩니다.
- 변수 오염 방지: 서로 다른 코드 블록에서 동일한 이름의 변수를 사용하더라도, 각 블록에서 선언된 변수만 사용하기 때문에 변수 오염을 방지할 수 있습니다.
- 코드 가독성 향상: 변수의 유효 범위가 명확하게 정의되어 코드를 이해하기 쉽습니다.
- 재귀 함수 구현: 렉시컬 스코프를 통해 내부 함수가 외부 함수의 변수를 참조할 수 있어 재귀 함수 구현이 용이합니다.
5. 렉시컬 스코프 예시
function outerFunction() {
let outerVar = 10;
function innerFunction() {
console.log(outerVar); // 10 출력
}
innerFunction();
}
outerFunction();
위 예시에서 innerFunction 함수는 outerFunction 함수 내에서 선언되었기 때문에 outerVar 변수에 접근할 수 있습니다.
6. 렉시컬 스코프 관련 주의 사항
- 렉시컬 스코프는 코드 작성 위치에 따라 변수와 함수의 유효 범위를 결정하기 때문에, 코드 구조를 이해하지 않고 사용하면 오류가 발생할 수 있습니다.
- var 키워드는 블록 스코프가 아닌 함수 스코프를 따르므로, 새로운 코드에서는 let 또는 const 키워드를 사용하는 것이 좋습니다
7. 결론
- 함수를 어디서 호출하는지가 아니라 / 어디에 선언하였는지에 따라 결정
- 함수를 어디서 정의했는지에 따라 상위 스코프를 결정
- 어디서 호출됐는지가 아니라 어디에서 정의했는지에 따라 상위 스코프를 결정
var name = 'zero';
function log () {
console.log(name); // nero
}
function wrapper () {
name = 'nero';
log();
}
wrapper();
var name = 'zero';
function log () {
console.log(name); // zero
}
function wrapper () {
var name = 'nero';
log();
}
wrapper();
var name = 'zero';
function outer () {
console.log('외부', name);
function inner () {...}
inner();
}
outer();
Var 실행 순서
// 사례 1
console.log(a) // ƒ a(){}
var a = 1
function a () {}
var a = 10
console.log(a) // 10
// 다음과 같이 렌더링
var a
a = function () {} // 이제 값을 함수로 보유
console.log(a) // f a(){}
a = 1 // a는 값 1을 보유하는 var
a = 10 // a는 값 10을 보유하는 var
console.log(a) // 10
// 사례 2
var a = 1
if (true) {
function a () {}
var a = 10
}
console.log(a)
// Uncaught SyntaxError: Identifier 'a' has already been declared
// 잡히지 않은 구문 오류: 식별자 'a'가 이미 선언되었습니다.
// 다음과 같이 렌더링
var a
a = 1
if (true) {
a = function() {}
let a
// The function declaration in the block uses ES6 declaration semantics
// (like let or const), which does not allow re-declarations.
// 블록의 함수 선언은 재선언을 허용하지 않는 ES6 선언 의미론
// (예: let 또는 const)을 사용합니다.
var a
// throws Uncaught SyntaxError: Identifier 'a' has already been declared
// 잡히지 않은 구문 오류 발생: 식별자 'a'가 이미 선언되었습니다.
a = 10
}
console.log(a)
function a () {}
var a = 10
// no error
// 사례 3
var a = 1
if (true) {
function a(){}
a = 10
}
console.log(a) // a(){}
// 다음과 같이 렌더링
var a
a = 1
if (true) {
a = function() {}
let a
a = 10
}
console.log(a) // f a(){}
// 사례 4
var a = 1;
if (true) {
var a = function () {}
a = 10;
}
console.log(a)
// 10
// 다음과 같이 렌더링
var a;
a = 1;
if (true) {
a = function () {}
a = 10;
}
console.log(a) // 10
// 사례 5
var a = 1;
if (true) {
function a () {};
a = 10;
console.log(a) // 10
}
console.log(a) // f a(){}
// 다음과 같이 렌더링
var a;
a = 1;
if (true) {
a = function() {};
let a;
a = 10;
console.log(a) // 10
}
console.log(a) // f a(){}
(function () {
var sahil = {
checkThis: function () {
console.log(this)
function checkOther () {
console.log(this)
}
checkOther()
// checkThis() function called in "global context", will return "this" as "window"
// "전역 컨텍스트"에서 호출된 checkThis() 함수는 "this"를 "window"로 반환합니다.
}
};
var moo = sahil.checkThis
moo()
})
// function called in "global context", will return "this" as "window"
// "전역 컨텍스트"에서 호출된 함수는 "this"를 "window"로 반환합니다.
렉시컬 스코프 다양한 예시: 심층 이해를 위한 가이드
렉시컬 스코프는 코드 작성 위치에 따라 변수와 함수의 유효 범위를 결정하는 방식입니다. 이는 코드 가독성을 향상시키고 변수 오염 문제를 방지하는 데 도움이 됩니다.
1. 기본 예시: 블록 스코프와 함수 스코프
function outerFunction() {
let outerVar = 10; // 블록 스코프 변수
function innerFunction() {
let innerVar = 20; // 블록 스코프 변수
console.log(outerVar); // 10 출력 (외부 변수 접근)
}
innerFunction();
console.log(innerVar); // ReferenceError: innerVar is not defined (내부 변수 접근 불가능)
}
outerFunction();
console.log(outerVar); // ReferenceError: outerVar is not defined (외부 변수 접근 불가능)
- outerFunction 내에서 선언된 outerVar는 outerFunction 블록 안에서만 유효합니다.
- innerFunction 내에서 선언된 innerVar는 innerFunction 블록 안에서만 유효합니다.
- innerFunction은 outerVar에 접근할 수 있지만, outerFunction은 innerVar에 접근할 수 없습니다.
2. 재귀 함수 예시: 렉시컬 스코프 활용
function factorial(n) {
if (n === 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
console.log(factorial(5)); // 120 출력
- factorial 함수는 자기 자신을 호출하는 재귀 함수입니다.
- 렉시컬 스코프 덕분에 내부 함수는 외부 함수의 매개변수인 n에 반복적으로 접근할 수 있습니다.
3. 클로저 예시: 렉시컬 스코프 보존
function makeCounter() {
let count = 0;
function increment() {
count++;
console.log(count);
}
return increment;
}
const counter1 = makeCounter();
const counter2 = makeCounter();
counter1(); // 1 출력
counter1(); // 2 출력
counter2(); // 1 출력
counter2(); // 2 출력
- makeCounter 함수는 increment 함수를 반환합니다.
- increment 함수는 makeCounter 함수 내에서 선언되었기 때문에, count 변수에 접근할 수 있습니다.
- makeCounter 함수를 여러 번 호출해도 각각 생성된 increment 함수는 독립적인 count 변수를 유지합니다.
4. 스코프 체인 예시: 변수 검색 순서
let globalVar = 10;
function outerFunction() {
let outerVar = 20;
function innerFunction() {
let innerVar = 30;
console.log(globalVar); // 10 출력 (전역 변수)
console.log(outerVar); // 20 출력 (외부 변수)
console.log(innerVar); // 30 출력 (내부 변수)
}
innerFunction();
}
outerFunction();
- globalVar는 전역 스코프에 선언되어 있습니다.
- outerFunction 내에서 선언된 outerVar는 outerFunction 블록 안에서만 유효합니다.
- innerFunction 내에서 선언된 innerVar는 innerFunction 블록 안에서만 유효합니다.
- innerFunction은 먼저 현재 블록의 변수 (innerVar)를 검색하고, 그 다음 외부 블록의 변수 (outerVar), 마지막으로 전역 변수 (globalVar)를 검색합니다.
728x90
'웹개발 > javascript' 카테고리의 다른 글
[자바스크립트] 논리 널 병합 할당. ?? (0) | 2024.07.28 |
---|---|
[자바스크립트] 실행 컨텍스트 (Execution Context) 이해하기 (0) | 2024.07.21 |
[자바스크립트] 스코프 (Scope) 완벽 정리 (0) | 2024.07.21 |
[자바스크립트] 리터럴 (Literal) - 숫자, 문자열, 불리언, 객체, 배열 (0) | 2024.07.21 |
[자바스크립트] this 키워드 / call(), apply(), bind() 메서드 (0) | 2024.07.21 |