Programming/JavaScript & TypeScript

[JavaScript] closure (클로저) 란?

Bonita SY 2020. 10. 15. 18:36
728x90
반응형

클로저(closure)

함수와 함수가 선언된 어휘적 환경의 조합

 

엥 이게 무슨 말이지;; mdn 도와줘!!! 소스코드로 이해를 해보자.

 

예제)

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
//myFunc변수에 displayName을 리턴함
//유효범위의 어휘적 환경을 유지
myFunc();
//리턴된 displayName 함수를 실행(name 변수에 접근)

- "displayName()"이 실행되기 전에 "makeFunc()"에 의해 "displayName()"이 return되어 "myFunc" 변수에 저장됨 (클로저 형성)

- "myFunc" = 클로저

=> 그러므로 "name" 변수에 접근 가능 (어휘적 환경의 조합)

 

 

예제)

function makeAdder(x) {
  var y = 1;
  return function(z) {
    y = 100;
    return x + y + z;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);
//클로저에 x와 y의 환경이 저장됨

console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산

- makeAdder() ≒ 함수를 만들어내는 공장

- add5와 add10 = 클로저

- add5와 add10은 동일한 함수 본문 정의를 공유하지만 서로 다른 어휘적 환경을 저장

-- add5의 x는 5를 저장

-- add10의 x은 10을 저장

- y의 값 1이 100으로 변경

=> 클로저가 return된 후에도 외부 함수의 변수들에 접근이 가능!!!

∴ 클로저에 단순히 값 형태로 전달되는 것이 아님 => 객체 지향 프로그래밍과 비슷한 느낌을 줄 수 있음

 

 

클로저를 이용하여 private method 구현 = 모듈 패턴 (module pattern)

※ private method : 코드에 제한적인 접근만을 허용, 전역 namespace를 관리하는 방법을 제공하여 불필요한 method가 공용 interface를 남용하는 것을 방지

 

예제)

var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };   
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1

- counter.incrementcounter.decrementcounter.value 세 함수에 의해 공유되는 하나의 어휘적 환경을 생성

- 공유되는 어휘적 환경은 실행되는 익명 함수 안에서 만들어짐

- 익명 함수는 정의되는 즉시 실행됨

 

위 예제에서

- public funtion : increment(), decrement,() value()

- private variable : privateCounter

 

 

클로저를 이용하여 객체 지향 프로그래밍의 정보 은닉과 캡슐화

예제)

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /* 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* 2 */
counter1.decrement();
alert(counter1.value()); /* 1 */
alert(counter2.value()); /* 0 */

- counter1 클로저와 counter2 클로저는 서로의 privateCounter라는 private variable에 영향을 주지 않음

 

클로저 scope chain

클로저의 scope

1. 지역 범위 (Local Scope, Own Scope)

2. 외부 함수 범위 (Outer Functions Scope)

3. 전역 범위 (Global Scope)

 

예제)

// 전역 범위 (global scope)
var e = 10;
function sum(a){
  return function(b){
    return function(c){
      // 외부 함수 범위 (outer functions scope)
      return function(d){
        // 지역 범위 (local scope)
        return a + b + c + d + e;
      }
    }
  }
}

console.log(sum(1)(2)(3)(4)); // log 20

// 익명 함수 없이 작성할 수도 있다.

// 전역 범위 (global scope)
var e = 10;
function sum(a){
  return function sum2(b){
    return function sum3(c){
      // 외부 함수 범위 (outer functions scope)
      return function sum4(d){
        // 지역 범위 (local scope)
        return a + b + c + d + e;
      }
    }
  }
}

var s = sum(1);
var s1 = s(2);
var s2 = s1(3);
var s3 = s2(4);
console.log(s3) //log 20

 

 

클로저 잘못된 사용 방법

- let 키워드 사용 전에 클로저와 관련된 일반적인 문제는 loop 안에서 클로저가 생성되었을 때 발생

let을 생활화 합시다.

 

클로저와 성능

- 클로저가 필요하지 않은데 함수 내에서 또 다른 함수를 불필요하게 작성하는 것은 좋지 않음

=> 처리 속도와 메모리 소비 측면에서 스크립트 성능에 부정적인 영향을 미칠 수 있기 때문

 

- 일반적으로 객체나 클래스를 생성할 때, method는 객체 생성자에 정의되기 보다는 객체의 프로토타입에 연결되어야 함

∵ 생성자가 호출되거나 객체가 생성될 때마다 method가 다시 할당되기 때문

 

음, 그냥 타입 스크립트를 써서 클래스를 만들자!

 

출처

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures

 

728x90
반응형