스코프(Scope)와 클로저(Closure) 이해

박동건·2019년 11월 30일
15

JavaScript

목록 보기
2/8
post-custom-banner

JavaScript에 대한 깊은 이해를 하기 위해서는 클로저(Closure)에 대해 알아야 되며, 이를 알기 위해서는 스코프(Scope)에 대한 이해가 필요하다.

유효범위(스코프)는 자바스크립트 뿐만 아니라 모든 프로그래밍 언어에서 가장 기본적인 개념의 하나로 반드시 알고 넘어가야한다. 하지만, 자바스크립트의 유효범위는 다른 언어의 유효범위와 다르다고 한다.

1. 유효범위(Scope)

프로그래밍언어에서 유효범위는 어느 범위까지 참조하는 지를 뜻한다.

유효범위의 종류에는 크게 두 가지가 있다.

  • 전역 스코프 (Global scope) : 스크립트 전체에서 참조되는 것을 의미하며, 어느 곳에서든 참조 된다.

  • 지역 스코프 (Local scope) : 정의된 함수 내에서만 참조되는 것을 의미하며, 밖에서는 참조 되지 않는다.

유효범위의 특징을 크게 나열하면 다음과 같다.

  • 함수 단위의 유효범위(function-level-scope)
  • 변수명 중복 허용
  • 암묵적 선언(implied globals)
  • Lexical scoping(Static scoping)

1-1. function-level scope

function-level scope란 함수 코드 블럭 내에서 선언된 변수는 함수 코드 블럭 내에서만 유효하고 함수 외부에서는 유효하지 않다는 것이다. (ES6 이후, let 키워드를 쓰면 block-level scope를 사용할 수 있다.)

var x = 0;
{
  var x = 1;
  console.log(x); // 1
}
  console.log(x); // 1

let y = 0;
{
  let y = 1;
  console.log(y); // 1
}
  console.log(y); // 0

1-2. 변수명 중복 허용

글로벌 영역에 변수를 선언하면 이 변수는 어느 곳에서든지 참조할 수 있는 global scope를 갖는 전역 변수가 된다.

var global = 'global';

function foo() {
  var local = 'local';
  console.log(global); // global
  console.log(local); // local
}
foo();

console.log(global); // global
console.log(local); // Uncaught ReferenceError: local is not defined

1-3. 암묵적 전역 (implied globals)

명시적으로 변수 앞에 var를 붙여주지 않으면 암묵적 전역변수가 된다.

function foo() {
  x = 1;   // Throws a ReferenceError in "use strict" mode
  var y = 2;
}

foo();

console.log(x); // 1
console.log(y); // ReferenceError: y is not defined

1-4. Lexical scoping (Static scoping)

자바스크립트는 함수가 선언된 시점에서의 유효범위를 갖는다.

  • 예제1
var number = 1234

function printNumber() {
  console.log(number);
}

function wrapper() {
  number = 4321
  printNumber();
}
wrapper(); // 4321

printNumber함수를 실행하기 직전에 number의 값을 변경 해주었으므로 wrapper함수를 실행 시켰을 때에 값이 변경되었다.

  • 예제2
var number = 1234
function printNumber() {
  console.log(number);
}

function wrapper() {
  var number = 4321
  printNumber();
}
wrapper(); //1234

wrapper함수 안에서 var키워드를 사용하자 결과가 달라진다.

함수를 처음 선언하는 순간, 함수 내부의 변수는 자기 스코프로부터 가장 가까운 곳(상위 범위에서)에 있는 변수를 계속 참조하게 된다.

위의 예시에서는 printNumber 함수 안의 number 변수는 선언 시 가장 가까운 전역변수 number를 참조하게 된다.

그래서 wrapper 안에서 printNumber 호출해도 지역변수 number을 참조하는 게 아니라 그대로 전역변수 number 값인 1234가 나오는 것이다.

2. 스코프체인(Scope Chain)

새롭게 정의된 스코프는 상위의 스코프에 접근할 수 있다.

스코프 체인은 scope의 가장 내부에서 scope chain을 따라 바깥쪽으로 검색을 하게 된다.

image.png

3. 클로저(Closure)

  • 클로저는 함수가 선언된 환경의 (렉시컬) 스코프를 기억하여 함수가 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기술

  • 함수 안의 함수

// counter는 outer 함수
// changeCount는 inner함수
// 객체를 리턴하고 있고 객체 안에는 increase, decrease, show와 같은 inner함수들을 저장

const counter = function() {
  let count = 0;
  function changeCount(number) {
    count += number;
  }
  return {
    increase: function() {
      changeCount(1);
    },
    decrease: function() {
      changeCount(-1);
    },
    show: function() {
      alert(count);
    }
  }
};

// counter를 실행하면 outer함수 스코프를 기억하고 있는 클로저들이 담긴 객체를 반환
// counterClosure는 counter함수 내부에 정의된 count나 changeCount에 접근 가능

const counterClosure = counter(); 
counterClosure.increase(); // 
counterClosure.show(); // 1
counterClosure.decrease();
counterClosure.show(); // 0

개인적으로 생활코딩에서 설명하는 클로저가 가장 이해하기 좋았다.

내부함수는 외부함수의 지역변수에 접근 할 수 있는데 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근 할 수 있다.
이러한 메커니즘을 클로저라고 한다

++ 내부함수에서 외부함수에서 선언된 변수를 사용한다면 그 내부함수는 클로저이다.

4. 참조문서

  1. http://www.nextree.co.kr/p7363/
  2. https://devyj.tistory.com/25
  3. https://www.zerocho.com/category/JavaScript/post/5740531574288ebc5f2ba97e
  4. https://velog.io/@victor/Javascript-Closure-%ED%81%B4%EB%A1%9C%EC%A0%80
  5. https://meetup.toast.com/posts/86
  6. https://opentutorials.org/course/743/6544
profile
박레고의 개발 블로그
post-custom-banner

5개의 댓글

comment-user-thumbnail
2019년 12월 3일

굿!잘보고가요

1개의 답글
comment-user-thumbnail
2021년 9월 17일

설명을 조금 더 가미하자면...

클로저라는 개념을 조금 더 낮을 레벨에서 봤을 때,
모든 변수들은 더 이상 참조되지 않을 때 GC에게 수집되어 삭제가 됩니다.
참조가 되고 있다면 수집 대상에서 제외되구요.

말씀해 주신 상황을 토대로 보자면, 외부 함수가 종료되더라도 내부 함수에서 변수를 계속 참조하고 있기 때문에 GC의 수집 대상에서 제외가 되어 접근이 가능해지는 것이죠!

이런 특징 때문에 클로저라는 현상이 생긴 것 같아요 :)

1개의 답글
comment-user-thumbnail
2022년 4월 1일

기존에 알고 있었던 스코프에 대해서 다시 한번 복습 하면서도 들어는 봤으나 생소했던 클로저에 대해 제대로 알고갑니다!!

답글 달기