CodeStates/JavaScript
Section1 / Unit9 : 스코프 & 클로저
yeeendy
2023. 4. 27. 10:54
스코프 학습목표
- 스코프의 의미와 적용 범위를 이해한다.
- 스코프의 주요 규칙을 이해한다.
- 전역 스코프와 지역 스코프의 차이를 이해한다.
- block scope와 function scope의 차이를 이해한다.
- 변수 선언 키워드(let, const, var)와 스코프와의 관계를 설명할 수 있다.
- 전역 객체가 무엇인지 설명할 수 있다.
스코프
- "변수 접근 규칙에 따른 유효 범위"
- 변수에 접근할 수 있는 범위
- 범위가 중괄호(블록) 또는 함수에 의해 나누어지고, 그 범위를 스코프라고 부른다
스코프의 규칙
- 바깥쪽 스코프에서 선언한 변수는 안쪽 스코프에서 사용 가능
- 안쪽에서 선언한 변수는 바깥쪽 스코프에서는 사용할 수 없음
- 중첩이 가능
- 가장 바깥쪽의 스코프는 전역 스코프(Global Scope)
- 전역이 아닌 다른 스코프는 전부 지역 스코프(local scope)
- 지역 변수는 전역 변수보다 더 높은 우선순위를 가짐
스코프의 종류
- 블록 스코프(block scope) : 중괄호를 기준으로 범위가 구분
- 함수 스코프(function scope) : function 키워드가 등장하는 함수 선언식 및 함수 표현식
- ❌유의❌ 화살표 함수는 블록 스코프로 취급
변수 선언 키워드(let, const, var)
let | const | var | |
유효 범위 | 블록 스코프 및 함수 스코프 | 블록 스코프 및 함수 스코프 | 함수 스코프 |
값 재할당 | 가능 | 불가능 | 가능 |
재선언 | 불가능 | 불가능 | 가능 |
- var 키워드는 for 문이 만들어낸 블록 스코프를 무시 (화살표 함수의 블록 스코프는 무시하지 않음)
- 블록 단위로 스코프를 구분했을 때, 훨씬 더 예측 가능한 코드를 작성할 수 있으므로 let 키워드의 사용을 권장
- var 키워드보다 let 키워드가 더 안전한 이유
->var 키워드는 재선언을 해도 에러가 나지 않지만, let 키워드는 재선언을 방지 - const 키워드는 값이 변하지 않는 상수를 정의할 때 사용
->값을 새롭게 할당할 일이 없으면 const 키워드 사용 권장
window 객체
- 브라우저의 창(window)을 의미하는 객체
- 전역 영역을 담고 있음
- var로 선언된 전역 변수와 전역 함수가 window 객체에 속함
전역 변수
- 어디서든 접근 가능한 변수 (가장 바깥 스코프에 정의한 변수)
- 편리한 대신, 다른 함수 혹은 로직에 의해 의도되지 않은 변경이 발생할 수 있음
->부수 효과(side effect) 발생 - 전역 변수는 최소화 할 것
- 선언 없이 변수를 할당하지 말 것
->선언 없이 변수를 할당하면, 해당 변수는 var로 선언한 전역 변수처럼 취급 - 'use strict'
->js 파일 상단에 'use strict' 라고 입력하면 "선언 없는 변수 할당"의 경우도 Strict Mode는 에러로 판단
클로저 학습목표
- 클로저 함수의 정의와 특징에 대해서 이해할 수 있다.
- 클로저가 갖는 스코프 범위를 이해할 수 있다.
- 클로저를 이용해 유용하게 쓰이는 몇 가지 패턴을 이해할 수 있다.
클로저
- 함수와 함수가 선언된 어휘적(lexical) 환경의 조합
- 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성
- 함수와 그 함수 주변의 상태의 주소 조합
- 함수와 그 함수가 접근할 수 있는 변수의 조합
const globalVar = '전역 변수';
function outerFn() {
const outerFnVar = 'outer 함수 내의 변수';
const innerFn = function() {
return 'innerFn은 ' + outerFnVar + '와 ' + globalVar + '에 접근할 수 있습니다.';
}
return innerFn;
}
//함수 outerFn에서는 변수 globalVar에 접근 가능
//함수 innerFn에서는 변수 globalVar와 함수 outerFn 내부의 outerFnVar에 접근 가능
//함수 outerFn과 outerFn에서 접근할 수 있는 globalVar
//함수 innerFn과 innerFn에서 접근할 수 있는 globalVar, outerFnVar
- 클로저의 함수는 어디에서 호출되느냐와 무관하게 선언된 함수 주변 환경에 따라 접근할 수 있는 변수가 정해진다
innerFn 함수가 최초 선언되었던 환경에서는 outerFnVar에 접근할 수 있기 때문입니다.
innerFnOnGlobal은 innerFn의 주소값을 가지고 있고, innerFn은 클로저로서 outerFnVar에 접근할 수 있기 때문입니다.
이 “환경”을 어휘적 환경(Lexical Environment)라고 합니다. - 클로저는 주로 데이터를 보존하는 함수, 커링, 모듈 패턴으로 활용한다.
- 클로저를 이용하면 특정 함수가 데이터를 보존할 수 있다.
- 커링을 이용하면 함수의 일부만 호출하거나, 일부 프로세스가 완료된 상태를 저장할 수 있다.
->커링은 여러 전달인자를 가진 함수를 함수를 연속적으로 리턴하는 함수
->커링은 함수의 일부만 호출하거나, 일부 프로세스가 완료된 상태를 저장하기에 용이
//sum 함수는 두 전달인자(10, 20)를 덧셈하는 함수
function sum(a, b) {
return a + b;
}
//currySum은 첫 번째 전달인자 10을 리턴하는 함수로 전달
//sum과 currySum이 같은 값을 리턴하기 위해서는
//currySum 함수에서 리턴한 함수에 두 번째 전달인자 20을 전달하여 호출하면 됩니다.
function currySum(a) {
return function(b) {
return a + b;
};
}
console.log(sum(10, 20) === currySum(10)(20)) // true
function makePancake(powder) {
return function (sugar) {
return function (pan) {
return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
}
}
}
const addSugar = makePancake('팬케이크가루');
const cookPancake = addSugar('백설탕');
const morningPancake = cookPancake('후라이팬');
// 잠깐 낮잠 자고 일어나서 ...
const lunchPancake = cookPancake('후라이팬');
-------------------------------------------------------------------------
function makePancakeAtOnce (powder, sugar, pan) {
return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
}
const morningPancake = makePancakeAtOnce('팬케이크가루', '백설탕', '후라이팬')
// 잠깐 낮잠 자고 일어나서 만든 팬케이크를 표현할 방법이 없다.
- 모듈 패턴을 이용하면 특정 데이터를 다른 코드의 실행으로부터 보호할 수 있다.
->모듈은 하나의 기능을 온전히 수행하기 위한 모든 코드를 가지고 있는 코드 모음
->하나의 단위로서 역할
->다른 모듈에 의존적이지 않고 독립적 - 기능 수행을 위한 모든 기능을 갖추고 있어야 하고, 또한 외부 코드 실행을 통해서 모듈의 속성이 훼손받지 않아야 합니다.
//계산기의 최소한의 기능을 모듈 패턴
function makeCalculator() {
//displayValue는 makeCalculator의 코드 블록 외에 다른 곳에서는 접근이 불가능
let displayValue = 0;
return {
add: function(num) {
displayValue = displayValue + num;
},
subtract: function(num) {
displayValue = displayValue - num;
},
multiply: function(num) {
displayValue = displayValue * num;
},
divide: function(num) {
displayValue = displayValue / num;
},
reset: function() {
displayValue = 0;
},
display: function() {
return displayValue
}
}
}
//cal의 메서드는 모두 클로저의 함수로서 displayValue에 접근할 수 있습니다.
const cal = makeCalculator();
cal.display(); // 0
cal.add(1);
cal.display(); // 1
console.log(displayValue) // ReferenceError: displayValue is not defined
- 클로저는 특정 데이터를 다른 코드의 실행으로부터 보호해야 할 때 용이