클로저(Closure) 란?

hoo00nn·2020년 12월 25일
0
post-thumbnail

이번 포스팅은 https://dev.to/shimphillip/javascript-closure-simply-explained-1f79 포스팅을 참고하여 작성되었습니다.

이번에는 어렵다고 소문이 난 클로저(Closure)에 대해서 한 번 알아보겠다.

클로저(Closure) 란?

클로저(closure)는 스코프를 계속 들고 있는 것이다. 본래 함수 내부에 선언한 변수는 함수가 끝나면 사라지지만, 클로저가 스코프를 계속 들고 있으므로 그 함수 내부의 변수를 참조할 수 있게 된다. (때문에 메모리 문제의 주범이기도 하다)

한 가지 예를 들어보겠다.

아래의 코드를 실행하면 greeting 변수는 함수가 끝나 사라졌지만 hello 변수를 통해 greeting 변수의 값이 출력되는 것을 볼 수 있다.

function sayHello() {
	const greeting = "Hello World"
	
	return function() {
		console.log(greeting); 
	}
}

const hello = sayHello(); // return 받은 함수를 저장한다.
hello(); // "Hello World" 가 출력된다.

즉, 클로저는 다른 함수에 의해 반환되는 상태 저장 함수이고, 부모 함수가 실행을 마친 경우에도 부모 범위의 변수와 매개 변수를 기억하는 컨테이너 역할을 한다고 생각을 하면 이해하기 쉬울 것이다.

클로저(Closure) 어디에 쓰일까 ?

내부에 선언한 변수를 기억하는 클로저(Closure)는 어디에 쓰일까?

1. 변수를 비공개로 유지할 수 있다.
아래의 코드에서 count 변수에 접근하려하면 count 변수가 전역환경에 노출되어 있지 않기 때문에 참조 오류가 발생한다.

function counter() {
  let count = 0;
  return function() {
    count += 1;
    return count;
  }
}


const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
console.log(count) // Reference error: count is not defined

2. 재사용 가능한 상태

function counter() {
  let count = 0;
  return function() {
    count += 1;
    return count;
  }
}

const incrementBananaCount = counter();
const incrementAppleCount = counter();
console.log(incrementBananaCount()); // 1
console.log(incrementBananaCount()); // 2
console.log(incrementAppleCount()); // 1

3. 모듈 설계 패턴
getName과 changeName만을 반환하기 때문에 name변수를 직접 변경할 수 없다. 오로지 할 수 있는건 getName을 통한 name변수에 접근, changeName을 통한 name변수의 값 변경이다.

let Dog1 = (function() {
  let name = "Suzy";

  const getName = () => {
    return name;
  }

  const changeName = (newName) => {
    name = newName;
  }

  return {
    getName: getName,
    changeName: changeName
  }
}())

console.log(name); // undefined
Dog1.getName() // Suzy
Dog1.changeName("Pink")
Dog1.getName() // Pink

문제

아래의 코드를 실행한 결과는 무엇일까 ?

for(var i=0; i<5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 1000)
}

정답은 1초 후에 5가 5번 찍힌다

5
5
5
5
5

우리는 0, 1, 2, 3, 4 순서대로 찍히기를 원한다. 클로저의 특성을 활용하여 해결해 보자.

for (var i = 0; i < 5; i++) {
  return setTimeout((function(index) {
      return function() {
        console.log(index);
      }
    }(i)), 1000);
  }
}

커링 기법을 사용해 i를 인자로 넘겨주고 index라는 변수로 받아서 사용하고 있다. 또한 클로저로 선언된 익명함수에선 그 함수가 선언된 당시의 실행환경을 기억하고 있기 때문에 각 시점의 index 값을 기억하게 되고, 그로 인해 우리가 원하면 0, 1, 2, 3, 4 순서대로 콘솔이 찍히게 되는 것이다.

마지막으로 클로저가 아닌 또 다른 형태로 해결할 수 있다.

for(let i=0; i<5; i++) {
  setTimeout((function() {
    console.log(i)
  }), 1000)
}

let 키워드는 블록 범위의 바인딩이 되기 때문에 우리가 원하는 출력을 얻을 수 있다.

profile
😀 신기술에 관심이 많고, 함께 성장하고 함께 개발하고 싶은 개발자가 되고 싶습니다. 😀

0개의 댓글