Skip to content

자신있게 JavaScript 클로저를 사용하는 방법

BbangYa edited this page Aug 20, 2019 · 1 revision

Kyle Simpson이 말하길

JavaScript 안에서 모든 사용자 주변에 클로즈가 있다. 당신은 그것을 인식하고 받아 들여야 한다.

당신이 알고있는지 모르는지는 모르겠지만 JavaScript를 작성할 때 클로저는 발생한다.
의도적으로 클로저를 만들고 그것을 활용하기 위한 클로저를 이해하는 것은 모든 JavaScript 개발자가 해결해야할 과제이다.

당신이 클로저를 볼 때 학문적인 정의는 이해하거나 찾을 수 있게 도와줄 것이다.
클로저는 함수 스코프 밖에서 실행될 때에도 함수가 외부 함수의 변수를 기억하고 접근 할 수 있다.

클로저는 함수가 스코프 밖에서 실행될 때에도 함수가 외부 함수의 변수를 기억하고 접근 할 수 있는 경우입니다.

우리는 잠시 후 스코프 개념을 알아볼 것 입니다. 코드에서 변수의 위치에 따라 일부 함수는 점근 할 수 있고 일부는 접근하지 않을 것입니다.

변수에 대해 이미 알고 있듯이 변수에는 이름과 값의 두가지 구성요소가 있습니다. "variable"이라는 이름은 매우 명확합니다.
값은 순간마다 다를 수 있지만 우리는 나중에 사용하기 위해 특정 시점의 값을 유지해야 합니다.

JavaScript에서 예를 들어 setTimeOut() 및 setInterval()과 같은 함수로 작업 할 때 자주 발생합니다.

for문 예제

카운터를 표시할때 사용되는 for 반복문과 함수 실행을 지연시킬 때 사용되는 setTimeout() 함수를 예로 들어 보겠습니다.
코드 스니펫을 보면 일반적으로 초당 0에서 9까지 출력한다고 가정했을 때, 실제로 이 코드를 실행하면 1초 간격으로 10이 10번 출력됩니다.
(본문 코드 이미지 깨짐)

왜 이런 일이 일어날까요?

for문이 실행되고 setTimeout()이 나중에 호출 될 때, 변수 i의 값은 이미 for문에 의해 끝까지 증가되었습니다.

이것은 변수가 다시 수정되기 전 주어진 시간에 변수 값을 저장하고 접근 해야하는 경우입니다.
각 반복에서 해당 i값을 '캡처' 하고 나중에 사용하기 위해 저장해야 합니다. 클로저가 이를 수행하는데 도움이 됩니다.
루프가 반복될 때마다 새로운 클로저 스코프를 만들기 때문입니다.

이 예제로 돌아가서 루프에 클로저를 추가하는 방법을 살펴봅시다.
클로저 작업에 대해 자세히 알아보기 전, 잠시 우회하여 클로저를 완전히 이해하기 위해 몇 가지 중요한 개념을 살펴 보겠습니다.

함수 또한 변수이다.

함수는 특별한 변수입니다. 값을 다시 할당하여 다른 함수에 매개변수로 전달할 수 있습니다.
(콜백 인자로 전달되는 비동기 함수를 작성하는데 유용합니다.)

변수의 스코프

함수 외부에서 정의 된 변수는 해당 함수로 접근 할 수 있습니다. 그것들은 함수 자체뿐만 아니라 그 함수 외부에서 수정 될 수 있습니다.

함수 내부에 정의 된 변수와 함수에 전달 된 이자는 함수 내에서만 접근이 가능합니다.

변수를 함수에 전달

변수가 함수에 인자로 전달되면 변수 값이 인자 안에 복사가 됩니다.

Taking closures by storm

이제 다시 돌아 왔으므로 클로저를 다룰 준비가 되었습니다.
다음 코드를 고려해 봅시다.

이 기능에 대해 좀 더 자세히 살펴 보자. 여기서 무슨 일이 있을까요?

함수에 전달 된 변수의 값을 취하는 인자 number1을 정의합니다.

함수 myFunction내에 함수를 정의합니다.
이 중첩 함수에서 변수 number1은 상위 함수 myFunction에서 add 함수 외부에 정의되었으므로 접근 할 수 있습니다.

add 함수를 반환하지만 호출하지는 않습니다. 이것은 무엇을 의미하나요?

myFunction 함수는 add 함수라는 함수를 반환합니다. 콘솔에 myFunction을 출력하면 함수의 반환 결과가 표시됩니다.

이것이 왜 중요한가요?
number1에 값을 할당 할 수 있음을 의미하므로 myFunction에 전달하면 작업을 완료하고 add 함수를 호출 할 준비가 되면 나중에 사용할 수 있도록 새로운 number1 값이 저장됩니다.

클로저에 대한 멋진 점은 다음과 같습니다.
응용 프로그램에서 특정 시점에 데이터를 캡처 할 수 있는 중간 상태로 함수를 작성할 수 있으며 나중에 데이터를 다른 시점에 사용할 수 있습니다.
클로저를 사용하는 것은 함수 안에 "일시정지" 버튼을 추가하는 것과 같습니다.
나중에 앱으로 변경 한 경우 해당 어플리케이션으로 변경하기 전에 데이터 값을 검색 할 수 있는 경우 다시 되돌릴 수 있습니다.

클로저가 없으면 JavaScript는 모든 코드를 실행하고 데이터의 마지막에 알려진 값을 반환하므로 데이터에 현재 사용하려는 값이 있던 순간으로 돌아갈 수 없습니다.

이것이 첫 번째 for문 예제에서 일어난 일입니다.
setTimeout 함수는 늦게 도착했습니다. for문은 이미 실행되었고 setTimeout이 도착했을 때 i 변수는 루프의 최종값인 10이 할당 되어 setTimeout에 도달할 때까지 계속 유지되었습니다.

클로저를 언제 사용해야할지 아직 확실하지 않습니까?
예를 들어 케이크를 굽는 방법으로 클로저가 어떻게 동작하는지 설명하겠습니다.

클로저로 케이크 굽기

우리는 다른 재료와 요리 온도를 가진 케이크를 굽는 기능을 사용하기 위해 아래 코드를 사용할 것입니다.

overTemperature() 함수는 함수 내부의 함수인 클로저로, bakeCake() 함수가 호출 된 후 언제든지 호출 할 수 있습니다.

콘솔에서 전체 로그를 가져 오기 위해 두 단계를 수행해야 하는 방법에 주목하십시오.
이 코드를 실행하고 chocolateCake() 또는 carrotCake() 실행하지 않으면 콘솔은 다음과 같이 출력됩니다.

"chocolate cake : add chocolate to the batter"
"carrot cake : add carrot to the batter"

오류가 발생하지는 않지만 함수 내부의 기능인 클로저는 실행되지 않으며 베이킹이 완료되지 않습니다.

또한 bakeCake() 함수를 사용하여 두개의 다른 케이크를 구울 수 있으며, 각 케이크는 나중에 사용하기 위해 자체 성분 인자를 기억하는 bakeCake()의 별도 인스턴스입니다.

실제 레시피와 마찬가지로 반죽에 재료를 추가하는 것으로는 충분하지 않으며, 완벽한 케이크를 만들기 위해 정확한 온도와 베이킹 시간을 설정해야합니다.
이를 위해서 함수 내부에서 다른 함수를 호출해야합니다.
add() 함수를 사용하여 앞의 예제에서 보았듯이 내부 함수가 호출되지 않으면 외부 함수의 반환은 단순히 결과 값이 아니라 호출 될 때까지 기다리는 또 다른 함수입니다.

여기에서 bakeCake() 함수는 두개의 인자와 함께 ovenTemperature() 함수를 호출 할 때까지 "베이킹 준비 완료" 를 반환하지 않습니다.
제대로 호출 될때까지 보류됩니다.

초콜릿을 반죽에 넣고 반죽을 놔둔 다음 레시피 북에서 정확한 온도와 베이킹 시간을 확인하는 데 필요한 모든 시간을 할애 할 수 있습니다.
한 시간 후, 조언을 얻기 위해 어머니에게 전화해야 할 수도 있습니다. 초콜릿이 든 반죽은 여전히 ​​여기에 있습니다.
즉, 언제든지 초콜릿이라는 성분 인자 인 chocolateCake()를 호출 할 것입니다.

준비가 되면 내부 함수를 어떻게 호출합니까?
어머니에게 전화를 걸어 인터넷의 모든 레시피를 확인한 후 이제이 케이크를 한 번에 마무리하겠습니다.

bakeCake() 함수의 인스턴스 두 개를 만들어 chocolateCake와 carrotCake라는 두 가지 변수에 할당 했습니다.

chocolateCake는 초콜릿 인수와 함께 함수 및 bakeCake의 인스턴스입니다.
chocolateCake가 "ready to bake"문장을 반환하려면, 그것을 호출하고 ovenTemperature() 함수에 필요한 인자를 전달하면됩니다.

이것은 우리가 클로저가 트리거되도록 두 번째 인수를 통과 할 때까지 chocolateCake가 실행되지 않음을 의미합니다.

여기에서 이해를 돕기 위해 bakeCake("chocolate")를 변수 chocolateCake에 할당 한 다음 두 번째 인수를 변수에 전달했습니다.
이 함수도 함수입니다. 그러나 레시피의 모든 요구 사항을 이미 알고 있다면 직접 갈 수 있습니다.

bakeCake("chocolate")(250, 60);

Douglas Crockford의 말에 따르면이 실험의 궁극적 인 결론은 다음과 같습니다.

"내부 함수는 외부 함수가 반환 된 후에도 항상 외부 함수의 변수 및 매개 변수에 접근 할 수 있습니다."

for문 예제에서 setTimeout()으로 돌아 가기

이제 bakeCake() 예제에서 알 수 있듯이 첫 번째 예제에서 코드를 수정하여 클로저를 추가하고 숫자 10을 10번 출력하는 대신
0에서 9까지 숫자를 출력하도록 하려면 어떻게 해야합니까?(10은 i의 마지막 알려진 값을 기억하십시오. 루프의 마지막에서 함수는 콘솔에 9를 기록한 다음이 수를 1 씩 증가시킵니다.)

비교를 위해 루프의 각 반복에서 i 카운터에 간단한 console.log() 및 이번에는 1 초 간격으로 동일하게 수행되는 setTimeout() 함수를 추가했습니다.

위의 이미지에서 setTimeout() 함수가 실제로 1 초 간격으로 결과를 출력하지만 i의 증가가 이미 발생했으며
setTimeout()이 실행되기 시작하면 변경되지 않은 값이 10임을 쉽게 알 수 있습니다.

우리는 이것을 어떻게 벗어나고 있습니까?

지금까지 클로저를 사용하여 배운 내용을 적용하고 setTimeout()의 외부 함수 역할을하는 timer() 함수를 작성하여 setTimeout()이 i 값에 접근 할 수 있게 하겠습니다.

그리고… 그것은 작동합니다! 여기에서 timer() 함수를 선언하고 그 안에 setTimeout() 함수를 래핑했습니다.
그런 다음 루프의 각 반복에서 카운터의 정확한 값을 전달하여 각 반복에서 함수 내부의 i 값을 "닫는"것입니다.

우리는 또한 쓸 수 있었습니다.

여기서 익명의 자체 호출 함수를 선언하고 루프의 각 반복에서 인자 i를 전달합니다.

이것은 우리가 만든 클로저 덕분에 1초 간격으로 0에서 9까지 숫자를 출력합니다.
주어진 루프 반복에서 i값을 캡처하고 함수 내부에서 닫고 값을 setTimeout()에 전달 한 다음 함수를 호출했습니다.

마무리

프론트 엔드에서 클로저를 사용하면 사용자의 클릭 이벤트에 매개 변수를 전달하거나 Google Maps API 결과 한도를 극복하는 등
많은 일을 할 수 있습니다.

중급 또는 고급 자바 스크립트 개발자가 되려면 클로저를 발견 한 후 희망적으로 사용하며 그렇지 않은 버그를 이해할 수 있습니다.

Clone this wiki locally