[javascript] Scope와 클로져(Closure)

Hyebin·2021년 3월 18일
0

Javascript

목록 보기
10/27
post-thumbnail

Scope란?


컴퓨터공학, 그리고 자바스크립트에서의 스코프도 "범위"의 의미를 가지고 있다. 다만, 조금 더 좁은 의미로 "변수의 유효범위"로 사용된다.

변수와 그 값이 어디서부터 어디까지 유효한지를 판단하는 범위
JS는 기본적으로 함수가 선언되는 동시에 자신만의 Scope를 가진다.

let greeting = 'Hello';

function greetSomeone() {
    let firstName = 'Nam';
    return greeting + ' ' + firstName;
}

greetSomeone();	//Hello Nam
firstName;
VM427:1 Uncaught ReferenceError: firstName is not defined
    at <anonymous>:1:1

이는 firstName이란 변수가 정의된 적 없다는 에러가 발생하는데 이 이유는 무엇일까?
안쪽에 접근 할 수가 없기 때문에
그게 바로 scope라는 개념이다.

Local Scope 안쪽에서 선언된 변수는 밖에서 사용할 수 없다.

1. Local Scope VS Global Scope

안쪽 Scope에서는 바깥 변수/함수를 접근할 수 있지만 바깥쪽 Scope에서는 사용 불가

  • Scope는 중첩이 가능하다 -> 함수 안에 함수를 넣을 수 있다.
  • Global Scope는 최상단의 Scope로 전역 변수는 어디서든 접근이 가능하다.
  • 지역 변수는 함수 내에서 전역 변수보다 더 높은 우선순위를 가진다.
let name = 'Richard';

function showName() {
    let name = 'Jack';
    console.log(name);
}

console.log(name);	//Richard
showName();	//Jack
console.log(name);	//Richard


let name = 'Richard';

function showName() {
    name = 'Jack';
    console.log(name);
}

console.log(name);	//Richard
showName();	//Jack
console.log(name);	//Jack

2. Function Scope VS Block Scope

Block : 중괄호로 시작하고, 끝나는 단위

보통의 경우에는 이 블록을 기준으로 Scope를 구분하는 단위로 생각할 수 있다.

for(let i=0; i<5; i++) {
  console.log(i); // 다섯번 iteration (0,1,2,3,4)
}
console.log('final i:', i); // ?

VM779:4 Uncaught ReferenceError: i is not defined
    at <anonymous>:4:25
(anonymous) @ VM779:4
  

for(var i=0; i<5; i++) {
  console.log(i); // 다섯번 iteration
}
console.log('final i:', i); // 5

3. 전역변수와 Window객체

전역 범위를 대표하는 객체에는 window 객체가 있다.
Global scope에서 선언된 함수, 그리고 var키워드를 이용해 선언된 변수는 window 객체와 연결된다.

var myName = 'Paul';
console.log(window.myName); //Paul

function foo() {
console.log('bar');
}
console.log( foo === window.foo); 	//true

전역 범위는 최상위 scope이니깐 여러 js파일을 만들다 보면 변수가 겹치는 경우가 생길 수도 있으니 되도록 하나의 scope를 만들어 지역 변수로 선언해 사용하는 것이 좋다.

let 키워드로 선언한 변수는 window 객체로 찾을 수 없다.

function foo() {
  console.log('foo Function()');
}
window.foo();	//foo Function()

var myName = 'paul';
window.myName;	//"paul"
  
let myLastname = 'coo';
window.myLastname;	//undefined

4. 선언 없이 초기화된 전역 변수를 쓰지 않기

var, let, const 키워드 없이 변수를 초기화하면 에러가 나지 않지만 전역변수로 취급된다. 아래와 같이 스코프 밖에서도 사용이 되는 것을 볼 수 있다.

function showAge() {
 //age가 전역 변수로 취급된다.
 age = 90;
 console.log(age);
}
showAge(); 	//90
console.log(age); //90

이것을 방지하려면 "use strict" 모드를 사용하면 문법적으로 실수 할 수 있는 부분들을 에러로 판단한다.

Closure란?


클로져는 "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다.

자바스크립트에서는 다른 컴퓨터 언어와는 조금 다른 특성을 종종 가지고 있는데 그 중 종종 사용되는 클로져라는 것이다.

여기서의 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"이다. 특이하게도 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경(어휘적 환경)을 기준으로 변수를 조회하려 한다.

클로저 함수는 외부 함수의 변수에 접근할 수 있는 내부 함수 또는 이러한 작동 원리를 일컫는다.

클로저 패턴은 일반 함수였다면 함수 실행 종료 후 가비지 컬렉션(참고 자료: Mdn 메모리 관리) 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 된다. 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문인데, 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있다.

아래 예시를 보면 변수 sum에 할당되어 있는 함수 안에 x가 외부 함수의 변수인데 접근이 가능하다.

Closure를 활용한 코딩 패턴

1) 커링

함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게하는 방법이다.

커링의 장점은 특정 변수 값을 고정해놓고 재사용할 수 있다.

function adder(x) {
 return function(y) {
   return x + y;
 }
}

//x와 y 인자값를 준다.
adder(2)(3); //5

let add100 = adder(100); // x의 값을 고정해놓는다.
add100(2); //102
add100(10);  //110

let add5 = adder(5); 
add5(2);  //7

2) 클로져 모듈 패턴

변수를 스코프 안쪽에 가두어 함수 밖으로 노출시키지 않는 방법이다. (캡슐화)
객체에 대한 키값을 함수로 정해준 형태이다.

프르그램 외부에서 type을 다른 값으로 바꾸게 되면 문제가 생긴다고 가정하면 간접적으로만 바꿀 수 있도록 만들어야 하는 상황이 발생할 수 있다. 이 때 클로져 모듈 패턴을 유용하게 쓸 수 있다.

function makePayment() {
  //은닉될 멤버 정의
  let type = '현금';	//반드시 현금 도는 카드여야 한다.
  
  return {
    payWithCash: function(amount) {
      type='현금';
      console.log(type + '으로' + amount + '만큼 지불합니다.');
    },
    payWithCard: function(amount) {
      type='카드';
      console.log(type + '으로' + amount + '만큼 지불합니다.');
    }   
  }
}  

0개의 댓글