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에 접근할 수 있기 때문입니다.
    innerFnOnGlobalinnerFn의 주소값을 가지고 있고, 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
  • 클로저는 특정 데이터를 다른 코드의 실행으로부터 보호해야 할 때 용이