TIL. 클로저 함수

김은서·2022년 7월 12일
0

TIL

목록 보기
9/52

클로저 함수

"함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다."

여기서 주목할 만한 키워드는 "함수가 선언"된 "어휘적(lexical) 환경".
특이하게도 자바스크립트는 함수가 호출되는 환경과 별개로 기존에 선언되어 있던 환경,
어휘적 환경을 기준으로 변수를 조회하려고 함.
이와 같은 이유로 "외부 함수의 변수에 접근할 수 있는 내부 함수"를 클로저 함수라고 합니다.

클로저 함수의특징

1. 함수를 리턴하는 함수

2. 외부 함수의 변수에 접근 가능한 내부 함수

클로저는 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분됨.
클로저의 핵심은 스코프를 이용해서 변수의 접근 범위를 닫는(closur; 폐쇄)데 있음
따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요함

ex)

변수 x와 y가 선언된 곳이 각기 다름.
x가 선언된 함수는 바깥쪽에 있으니 '외부 함수' 라고 부름.
변수 y가 선언된 함수는 보다 안족에 있으니 '내부 함수' 라고 부름

따라서, 이 클로저 함수는 스코프가 분리되어 있음

Q. 외부 함수는 y에 접근이 가능한가? NO! 바깥쪽 스코프에서 안쪽 스코프로의 접근은 불가.
Q. 내부 함수는 x에 접근이 가능한가? YES! 반대의 상황은 접근 가능.

3. 데이터르 보존하는 함수

일반적인 함수는 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없음.
하지만 클로저 함수는 외부 함수의 실행이 끝나도, 외부 함수 내 변수를 사용할 수 있음.
(외부 함수 내 변수가 메모리 상에 저장됨)

ex. 1) 
const adder = function (x){
	return function (y){
  	return x + y;
  }
}

const add5 = adder(5);
add5(7) //12
add5(10) // 15

--
함수 실행이 끝나도, 5라는 값은 사용가능!
ex. 2) 보다 실용적인 예제 : HTML 문자열 생성기

const tagMaker = tag => content => `<${tag}>${content}</${tag}>`

const divMaker = tagMaker('div')
divMaker('hello') //'<div>helllo</div>'
divMaker('codestates') //'<div>codestates</div>'

const anchorMaker = tagMaker('a');

anchorMaker('go') //'<a>go</a>'
anchorMaker('urclass') //'<a>urclass</a>'

4. 정보의 접근 제한

클로저 모듈 패턴

const makeCounter = () => {
	let value = 0;
    
    return{
    increase: () =>{
    	value = value +1
    },
    decrease: () =>{
    	value = value -1
    },
    getvalue: () => value
    }
}

const counter1 = makeCounter();
counter1 // {increase: f, decrease: f, getvalue: f} 함수 여러개를 포함한 객체

클로저를 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러개의 내부함수를 리턴하도록 만듬.

makeCounter를 실행하여 변수에 담아보면,
makeCounter함수는 increase, decrease, getvalue 메서드를 포함한 객체 하나를 리턴함
따라서, counter1은 객체임.

Q. makeCounter 함수를 바꾸지 않고, value라는 변수에 값을 새롭게 할당할 수 있는 방법이 있나요?
NO!
'외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'라는 스코프 규칙에 의해 어떤경우에도 'value'는 직접 수정 불가능.
대신, 리턴하는 객체가 제공하는 메서드를 통해 'value'값을 간접적으로 조작할 수 있음

=> 이것이 정보의 접근 제한 (캡슐화).

5. 모듈화

재활용 가능한 makeCounter함수
여러개의 counter를 만드는 것이 가능

const makeCounter = () => {
	let value = 0;
    
    return{
    increase: () =>{
    	value = value +1
    },
    decrease: () =>{
    	value = value -1
    },
    getvalue: () => value
    }
}

const counter1 = makeCounter();
const counter1 = makeCounter();
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue(); //1

const counter12 = makeCounter();
counter2.decrease();
counter2.decrease();
counter2.decrease();
counter2.getValue();//-3

makeCounter에 의해 리턴된 객체는,
makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 됨.

따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고,
각각의 값을 보존할 수 있음.

이와 같이 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 함.

클로저를 통해 데이터와 메서드를 같이 묶어서 다룰 수 있음.
즉, 클로저는 모듈화에 유리합니다.

Q.

Q1.

let x = 10;

function add (y) {
  return x + y;
}

function strangeAdd (x) {
  return add(x) + add(x);
}

let result = strangeAdd(5); 

result = 30

--
함수 strangeAdd는 매개변수 x를 통해 전달받은 값을 다시 add 함수에 전달하는 구조로 이루어져 있음. 
add 함수는 매개변수 y를 통해 받은 값을 전역에 선언된 x와 더하여 반환.

strangeAdd(5)를 실행하면 add(5) + add(5)가 반환되고, add(5)는 15를 돌려줌.
결과적으로 result의 값은 30.
Q2.

let add = function(x) {
  let sum = function(y) {
    return x + y;
  }
  return sum;
}

let foo = add(1);
foo(3);
let total = foo(6);

--
함수 add는 x를 매개변수로 하며, 함수 sum을 반환. 
add는 매개변수 x를 통해 전달받은 값을 내부 함수 sum에게 내려줌.
여기서 sum은 외부 함수인 add의 변수 x에 접근할 수 있으므로 클로저임.

total의 값인 foo(6), 은 add(1)(6)과 같습니다. 따라서 total은 7.

foo(3)은?
foo(3)이 실행되면 4가 반환되지만,
이 값은 어떠한 변수에도 할당이 되지 않았으므로 total에 아무런 영향을 미치지 않음.
Q3.

var a = 0;
function foo() {
    var b = 0;
    return function() {
        console.log(++a, ++b);
    };
}

var f1 = foo();
var f2 = foo();

f1(); // --> 1 1
f1(); // --> 2 2
f2(); // --> 3 1
f2(); // --> 4 2

--
여기서 변수 a 는 글로벌 변수 / 변수 b 는 로컬 변수로 볼 수 있음.

변수 b 는 함수 안에 선언되어 있기 때문에(= local 변수) f1()을 실행할 때,
f2()를 실행할 때 각각 카운트가 올라가는 것이고,

변수 a 는 함수 밖에 선언되어 있기 때문에(= global 변수)
f1(), f2() 중 어떤 것을 실행해도 카운트가 올라감.

0개의 댓글