JavaScript_ Callback과 Closure

Adela·2020년 4월 25일
2

JavaScript

목록 보기
5/17
post-thumbnail

콜백함수_Callback

JS에서 함수는 값으로 취급되기 때문에 함수를 함수의 parameter로도 받을 수 있다.

인자로 함수(Callback)를 넘겨 받은 함수는 이 콜백을 바로 실행할 수도 있고, 아니면 필요에 따라 나중에 실행할 수도, 실행하지 않을 수도 있다.

예시1

콜백 func를 x번 실행하는 함수
어떤 문자열을 x번 반복해서 출력하고 싶다.
= 출력하는 함수를 x번 반복 실행하면 된다.

필요한 함수

  • 문자열을 출력하는 함수
function printHelloWorld(){
  console.log('Hello World!');
}
  • 원하는 횟수만큼 반복하는 함수
    함수 f를 x번 반복 실행하는 함수
function repeatFunc(func, x){
  //func는 function type, x는 number type
  for(let i = 0; i< x; i++){
    func();
  }
}

이 둘을 조합해보면
반복 실행하는 함수에 출력 함수를 끼워 넣어야 하는 것을 알 수 있다.
반복하는 함수에 함수를 인자로 받는 매개변수 func가 있다.

원하는 결과를 3번 출력해보자!라고 한다면

repeatFunc(printHelloWorld, 3);

//또는
repeatFunc(function(){console.log('Hello World!')}, 2)
//따로 함수를 정의하지 않고 인자에 바로 콘솔에 문자열을 출력하는 익명함수와 반복할 횟수를 인자로 넣어준다

이것처럼 다른함수의 인자로 넘겨지는 함수를 Callback함수라고 한다.

예시2

어떤 문자열(url)을 가지고 1. 이미지를 다운로딩 하고
2. 다운로드한 이미지로 어떤 ~~ 동작을 하는 함수가 있다.

1번 함수는 주소값 url로 다운로드하는 걸리는 시간동안 실행되고
다운로드가 완료되면 다음 동작할 2번 함수를 callback으로 전달한다.

1번 함수를 통해 다운로드가 끝난 이미지를 콜백 2번 함수에 전달하면서 2번 callback함수를 호출하고 종료된다.

어떤로직이 진행되다가 다시 나중에 호출이 될 로직을 담고있는 함수를 콜백이라고한다.

콜백이 또 콜백을 받고, 그 콜백이 또 콜백을 받고…
함수가 여러번 중첩되면 가독성이 떨어지는 코드가 되는데 avaScript에서는 콜백의 간결한 표현과 제어를 위해서 Promise 라는 객체를 제공한다.

immersive에서 배우는 듯 하다.

클로저_Closure

외부함수의 실행이 종료되어도, 외부함수의 범위(scope)에 접근할 수 있는 내부함수

클로저는 자신이 생성될 때의 환경을 기억하는 함수이다.

JS에서 함수는 값으로 취급되기 때문에 함수를 parameter로도 받을 수 있고(Callback), 함수를 return할 수도 있다(Closure).

예시 1

function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);
  
  function innerFn() {
    //outerFn함수 내의 함수 =closure
    let innerVar = 'inner';
    console.log(innerVar);
  }
  return innerFn;
}

outerFn();
// 콘솔에 outer가 찍히고
// 리턴값은 아직 실행되지 않은 innerFn함수

outerFn()();
// 콘솔에 outer 그다음 inner 순서대로 찍힌다

let innerFn = outerFn();
// outer출력
// innerFn에는 outerFn()의 리턴값이 할당
// (실행되지 않은 innerFn함수)
innerFn(); // 콘솔에 inner 출력

예시 2

콜백 f를 x번 실행하는 함수를 생성하는 함수
함수를 반복실행하는 함수

callback 예시 1과 비슷

클로저를 활용하면
출력함수 f를 x번 실행할 수 있는 함수자체를 리턴하는 함수를 만들어볼 수 있다.

또 다른 function을 리턴하는 function

function printHelloWorld(){
  console.log('Hello World');
}

function makeRepeatFunc(f, x){
  return function (){  //일단 인자는 없다고하고
    for(let i=0; i< x; i++){
      f();
    }
  }
}

let printHelloWorld3 = makeRepeatFunc(printHelloWorld, 3);
// printHelloWorld는 클로저

makeRepeatFunc가 실행되고 바로 리턴되서 종료되었는데도 return된 print함수는 make함수의 변수를 참조할 수 있다.(에러가 안난다) => 실행하기 전에는 아무일도 일어나지않는다.

printHelloWorld3();
//문자열이 콘솔에 3번 반복출력된다

클로저를 이용해서 동적으로 함수를 생성할 수 있다.

클로저 모듈 패턴

변수를 은닉할 수 있다.
변수를 scope 안쪽에 가둬서 함수 밖으로 노출시키지 않도록 할 수 있다.

x값을 고정해놓고 재사용할 때 유용하다.

html에서 사용할 경우에는 외부함수의 변수가 저장되서 템플릿 함수처럼 사용가능하다.

function makeCounter() {
  let privateCounter = 0;
  
  return{
    increment: function() {
      privateCounter++;
    },
    decrement: function() {
      privateCounter--;
    },
    getValue: function() {
      return privateCounter;
    }
  }
}

let counter1 = makeCounter();  //이렇게 함수를 따로 선언해서 접근
counter1.increment();  // ++
counter1.increment();  // ++
counter1.getValue();   // --> 2

let counter2 = makeCounter();
counter2.increment();  // ++
counter2.decrement();  // --
counter2.increment();  // ++
counter2.getValue();   // --> 1
/*
전역에 선언된 counter1와 counter2는 다른 클로저를 가진다.
counter1의 클로저에 접근해서 여러번 increment()를 해도 값이 증가 및 감소하고
counter2의 클로저에 접근해서 increment()를 해도 counter1의 값에는 영향이 없다.
*/

뭔가 당연한 것 같은데 이론으로 보니까 어려운 것 같다.

커링

여러 개의 인자를 받는 함수가 있을 때 일부의 인자를 하나씩만 고정한 함수를 만드는 기법

모든 인자를 받을 때까지 계속 함수를 체인으로 생성해 결과적으로 값을 처리하도록 하는 것

전달인자가 3개라면 인자를 하나씩 3번 받아야 호출된다.

function multiplyThree(x) {
  return function(y) {
    return function(z) {
      return x * y * z;
     }
  };
}

multiplyThree(4)(8)(2); // 64

예시 1

function adder (x) {
  return function (y) {    // 클로저
    return x + y;
  }
}
adder(2)(3);  // 5

예시 1-2

let add100 = adder(100);
add100(2);   // adder(100)(2) --> 102
add100(10); // adder(100)(10) --> 110

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

예시 2 (html)

// html에서의 사용예시
function htmlMaker (tag) {
  let startTag = '<' + tag + '>'
  let endTag = '</' + tag + '>'
  return function (content) {    // 클로저
    return startTag + content + endTag;
  }
}

let divMaker = htmlMaker('div')
divMaker('안녕하세요')   // "<div>안녕하세요</div>"

let spanMaker = htmlMaker('span')
spanMaker('안녕')   // "<span>안녕</span>"
profile
👩🏼‍💻 SWE (FE) 🚀 Be GRIT!

0개의 댓글