javascript - 내부함수 / 외부함수

김동하·2020년 9월 19일
0

javascript

목록 보기
13/58

클로저란 내부함수가 외부함수의 context에 접근할 수 있는 것을 뜻함.

내부함수와 외부함수

function outter(){
   const title = "dongha"
   function inner(){
     
     console.log(title) // dongha
   }
   inner()
}

outter() 

inner는 내부 함수 inner 함수 기준으로 otter는 외부함수다. 함수 안에 함수를 선언하면 응집성이 높아진다.

function outter(){
   const title = "dongha"
   function inner(){
     console.log(title) // dongha
   }
   inner()
}
outter()

변수 title을 inner()에서 빼어 outter() 안에 정의했다. 변수 title은 외부함수에 정의되어있는 지역 변수다. inner() 함수 내부에서 title을 console 했을 때 inner() 내부에 변수가 없으므로 외부 함수에서 변수를 찾게 된다. 이렇게 내부 함수에서 외부 함수의 지역 변수에 접근하는 것을 클로져라고 한다.

function outter(){
  const title = "dongha"
  return function(){
    console.log(title) //dongha
  }
}

const inner = outter()
inner()

outter란 외부함수 안에 title이란 지역 변수가 있고 return 값은 내부 함수다. 내부 함수 안에서 console.log가 작동한다. inner = outter() 즉, inner는 outter()의 리턴값인

function(){
    console.log(title) //dongha
  }

이다. inner라는 변수에 존재하는 함수를 호출하면 dongha가 출력된다. 여기서 이상한 점은 outter()는 내부함수를 return 하는 동시에 종료됐다. 하지만 inner()를 호출하자 outter() 즉, 외부 함수에 존재하는 지역 변수를 가져왔다. 외부함수가 종료된 이후에도 내부함수를 통해 변수에 접근할 수 있는 것이 클로져 특성!

private variable

데이터에 접근할 수 없게 만든 변수다.

function today_meal(food){
    return {
        get_food : function (){
            return food;
        },
        set_food : function(_food){
            food = _food
        }
    }
}

pizza = today_meal('Bulgogi Pizza');
mandu = today_meal('Kimchi Mandu');
 
console.log(pizza.get_food()); // Bulgogi Pizza
console.log(mandu.get_food()); // Kimchi Mandu
 
mandu.set_food('우리동네만두가게'); 
 
console.log(pizza.get_food()); // Bulgogi Pizza
console.log(mandu.get_food()); // 우리동네만두가게

today_meal이라는 함수는 food를 파라메터로 받고 return 값은 객체다. 객체는 get_food와 set_food라는 키(메서드)를 가지고 있다. 이 메서드들은 내부함수다.

get_food의 value는 food를 return하는 함수다. get_food 메서드를 호출하면 today_meal의 파라메터를 return한다. 외부함수의 지역변수에 접근할 수 있기 때문이다.

set_food는 _food를 파라메터로 받는다. _food는 food이고 food는 내부 변수를 지칭하게 된다.

pizza와 mandu라는 변수를 만들고 각각 다른 인자를 today_meal 함수에 넣어 return값을 변수에 할당했다(맞는 말인지 모르겠음)

이제 각 변수의 메서드를 호출하면 각기 다른 값이 나온다. pizza.get_food(), mandu.get_food() pizza와 mandu는 똑같은 객체이지만 객체가 가지고 있는 메서드가 접근하는 외부 함수의 변수의 값은 다르다.

이제 객체 mandu에 set_food 메서드를 호출하고 '우리동네만두가게'를 argument로 주면 '우리동네만두가게' => _food => food가 되어 최종적으로 today_meal 함수의 지역 변수를 변경시킨다.

다시 get_food()를 두 객체에 했을 때 다른 값이 나오는 걸 알 수 있다. mandu의 메서드가 접근했을 때 값만 바뀌고 pizza가 접근했을 때의 값은 그대로다.

today_meal 함수는 return하는 동시에 종료되어 todya_meal의 지역 변수는 내부 함수를 통해서만 접근 가능하다.

클로져 응용

var로 변수를 선언했을 때와 let으로 선언했을 때 출력 값이 다르다. 두 개 다 해보고 왜 그런지 알아야 한다!

먼저 var로 선언했을 때(생코 강의에서 var로함)

var arr = []

for(var i = 0; i < 5; i++){
    arr[i] = function(){
        return i;
    }
}

for(var index in arr) {
    console.log(arr[index]()); // 5 5 5 5 5
}

arr 라는 배열이 있고 for문을 통해 배열의 원소로 함수를 정의한다. arr[i], 즉 arr의 원소들은 for문이 돌면서 i를 리턴하는 함수를 담는다. for in문에서 arr[index]() 배열에 담긴 함수 값을 호출하면 0,1,2,3,4가 호출될 거 같지만 5가 다섯 번 호출된다. 그 이유는 i 가 i를 return 하는 함수의 외부 변수가 아니기 때문이다. 즉, return i에서 i가 전역변수를 가리키고 있다.

함수 실행 순서를 살펴보자면 for문이 5회 돌면서 arr에는

function(){
     return i
}

가 다섯 개 담긴다. for문이 종료된 시점에서 i는 5가 되었고 이제 for in 문으로 넘어가 arr[index]()를 실행하는데 이때 i 값이 5이므로 5가 5번 출력되는 것이다. (어렵다)

제대로 된 값이 나오기 위해서는 외부 함수가 있어야 하고 그 외부 함수의 지역 변수 값을 내부 함수가 참조하도록 만들면 된다.


var arr = []

for (var i = 0; i < 5; i++) {
    arr[i] = function(id) {
        return function() {
            return id;
        }
    }(i)
}

이렇게 고쳐졌다. 일단 id 를 파라메터로 받는 외부함수를 만들었고 바로 호출한다. 그리고 거기에 arument로 i를 주었다. 그 외부 함수 안에 내부 함수를 return하는데 그 함수는 id를 return한다. 그 return 값이 배열에 담긴다. (모르겠다) 즉, for 문이 돌 때마다 내부함수를 리턴하고 내부함수는 외부 함수의 지역 변수를 리턴한다. 내부함수가 외부 함수의 지역 변수에 접근할 수 있었기 때문에 가능하다.

for 문에서 i 를 let으로 선언하면 0,1,2,3,4 의 값을 출력하는데 아마 let이 block scope라서 그런 거 같다.

더 공부하자!

출처 : https://www.inflearn.com/course/%EC%A7%80%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%96%B8%EC%96%B4-%EA%B8%B0%EB%B3%B8/lecture/2535?tab=note&mm=close

profile
프론트엔드 개발

0개의 댓글