재귀 함수

  • 프로퍼티 연동 방지
    • Object에 Object를 할당하면 프로퍼티 값이 연동된다.
      var origin = {member: 100};
      var dup = origin;
      dup.member = 200;
      console.log(origin.member); //200;
      
      • origin 오브젝트를 dup 변수에 할당한 후 dup.member에 200을 설정하면 origin.member 값이 연동되어 바뀐다.
      • 오브젝트를 할당하면 값을 공유하기 때문이다.
    • 배열도 마찬가지로 연동된다.
      var origin = [1, 2, 3];
      var dup = origin;
      dup[1] = 200;
      console.log(origin); // [1, 200, 3]
      
      • 배열도 마찬가지로 배열을 할당하면 값을 공유한다.
    • 연동방지: 프로퍼티 단위로 할당
      var origin = {member: 100};
      var dup = {};
      for(var name in origin) {
          dup[name] = origin[name];
      }
      dup.member = 200;
      console.log(origin.member); // 100
      console.log(dup.member); // 200    
      ``
      ```javascript
      var origin = [1, 2, 3];
      var dup = []
      for(var i = 0; i < origin.length; i++) {
          dup[i] = origin[i];
      }    
      dup[1] = 200;
      console.log(origin); // [1, 2, 3]
      console.log(dup); // [1, 200, 3]
      
      • 값의 공유를 방지하려면 프로퍼티 단위로 할당해야 한다.
  • 재귀함수
    • 함수 안에서 자신 함수를 호출하는 형태
    • 사용사례
      • {name: {name: {name: value} } }
      • [ [1, 2], [3, 4], [5, 6] ]
    • 재귀 함수 형태
      var book = {
          member: {name: 100},
          point: {value: 200}
      };
      function show(param) {
          for(var type in param) {
              typeof param[type] === "object" ? show(param[type]) : console.log(type + " : " + param[type]);
          }
      }
      show(book); // name : 100 / value : 200
      
      • show(book);
        • 마지막 줄에서 show(book)을 호출하면서 book 오브젝트를 파라미터 값으로 넘겨준다.
      • for(var type in param) {…}
        • fon-in으로 파라미터로 받은 오브젝트 전개
      • typeof param[type] === “object” ? show(param[type]) : console.log(type + “ : “ + param[type]);
        • value가 object 타입이면, 다시 show 함수 호출 아니면 key, value를 로그로 찍는다.
        • book[“member”], book[“point”] 의 value가 오브젝트이므로 다시 재귀 호출
        • member[“name”] 은 object가 아니므로 name : 100 을 로그로 찍는다.
        • member[“value”] 는 object가 아니므로 value : 200을 로그로 찍는다.

즉시 실행 함수

  • 즉시
    • 엔진이 함수를 만났을 때 자동으로 함수를 실행. 즉시에 실행하므로 즉시 실행 함수
      (function() { console.log("JS북"); } ());
      
    • IIFE: Immediately Invoked Function Expression
    • (function() {…}()) 형태
      • 함수 이름이 없으므로 함수 선언문, 함수 표현식도 아니다.
      • 문법 에러가 발생하지 않는다.
      • 무명함수, 익명함수라고도 부른다.
    • 함수 즉시 실행 과정
      • 표현식을 평가 소괄호()는 그룹핑 연산자
        var total = (1 + 2);
        console.log(total); // 3
        
        • (1 + 2) 형태에서 소괄호()는 그룹핑 연산자이며 1 + 2 는 표현식
        • 그룹핑 연산자는 소괄호 안의 표현식을 평가하고 평가 결과를 반환
        • 소괄호()와 표현식 평가가 키포인트
      • 함수 이름 필요
        • 함수에 이름이 없으면 문법 에러
          var value = function() {
            return 100;   
          };
          console.log(value()); // 100
          
        • 함수 표현식으로 엔진이 function 키워드를 만나면 function object를 생성하여 value 변수에 할당
        • value 변수를 선언하지 않으면 함수 이름이 없으므로 문법 에러, 함수 표현식도, 함수 선언문도 아니기 때문이다.
        • value()처럼 function 끝에 붙는 소괄호는 함수 호출 용도(그룹핑이 아니다)
      • 함수 표현식 끝에 소괄호 작성
        var value = function() {
            return 100;   
        }();
        console.log(value); // 100
        
        • 함수 끝에 소괄호를 첨부한 형태
        • function 키워드를 만나 function object 생성
        • 소괄호가 있으므로 함수 호출
        • 함수에서 반환한 100을 변수 value에 할당
      • 소괄호 안에 함수 작성
        var value = (function() {
            return 100;   
        }());
        console.log(value); // 100
        
        • 소괄호 안에 함수 작성
        • 소괄호는 그룹핑 연산자
        • 그룹핑 연산자이므로 소괄호 안의 표현식을 평가
        • 표현식이 함수이므로 function object 생성
        • function 끝에 소괄호가 있으므로 함수 실행
      • 그룹핑 연산자에서 반환된 값이 할당되는 변수를 작성하지 않은 상태
        (function() { console.log(100); }()); // 100
        
        • 그룹핑 연산자를 작성하지 않으면 함수 이름이 없으므로 문법 에러 발생
        • 하지만, 그룹핑 연산자를 작성하면 표현식에 function을 작성한 것이므로 문법 에러가 발생하지 않는다. 즉, (1+2)에서 1 + 2 대신에 함수를 작성한 것
        • 표현식과 표현식 평가 결과는 평가 결과가 반환할 때까지 메모리에 저장하고 평가 결과를 반환하면 지워진다.
        • ( 1 + 2 )의 결과가 메모리에 저장된다면 매우 많은 메모리가 필요할 것이다.
        • function(){}();
          • 코드로 만든 오브젝트도 메모리에 저장되지 않으며 실행 결과도 메모리에 저장되지 않는다.
          • 따라서 저장해야 할 것이 있다면 표현식 밖의 변수, 프로퍼티에 저장해야 한다.
          • 저장할 필요가 없는 1회성 코드이면서 엔진이 function 키워드를 만나는 시점에 즉시 실행해야 한다면 그룹핑 연산자 안에 표현식으로 작성
          • 무명 함수는 그룹핑 연산자 안의 코드를 한번만 사용할 때 사용. 주로 초깃값 설정할 때 사용

    Closure

    • Closure
      • function object를 생성할 때 함수가 속한 스코프를 [[Scope]]에 설정하고
      • 함수가 호출되었을 때 [[Scope]]의 프로퍼티를 사용하는 메커니즘
      • [[Scope]]의 설정과 사용방법을 이해하면 클로저는 단지 논리적인 설명
    • Closure 논리
      실행 콘텍스트(EC): {
          렉시컬 환경 컴포넌트(LEC): {
              환경 레코드(ER): {
                  선언적 환경 레코드(DER): {},
                  오브젝트 환경 레코드(OER): {}
              },
              외부 렉시컬 환경 참조(OLER): {}
          },
          ...
      }
      
      • 실행중인 function object에 작성한 변수, 함수를 선언적 환경 레코드(DER)에 설정
      • [[Scope]]의 변수, 함수를
        • 외부 렉시컬 환경 참조(OLER)에 바인딩
        • 변수 이름으로 접근하여 값을 사용하거나 변경할 수 있음
        • 함수로 호출할 수 있음
      • Closure 논리는 외부 렉시컬 환경 참조(OLER)에 있는 변수와 함수를 내것처럼 사용하는 것이다.
        function book() {
          var point = 100;
          var getPoint = function(param) {
              point = point + param;
              return point;
          }
          return getPoint;
        };
        var obj = book();
        console.log(obj(200)); // 300
        
        • var obj = book();
          • book()함수를 호출하면 엔진은 아래 방법으로 처리
          • getPoint()의 클로저가 만들어 진다.
        • 실행 콘텍스트(EC) 생성
        • 3개의 컴포넌트 생성
          • LEC, VEC, TBC
        • function object의 [[Scope]]를 외부 렉시컬 환경 참조에 바인딩
          • 초기화 단계
          • var point, var getPoint
            • 변수 선언적 환경 레코드(DER)에 설정. undefined 할당
          • var point = 100;
            • DER 속 point에 100 할당
          • var getPoint = function(param) { … };
            • function object 생성
            • 스코프를 [[Scope]]에 바인딩
            • point: 100이 [[Scope]]에 바인딩된다. - getPoint function의 EC에 외부 렉시컬 환경 참조(OLER)에 바인딩
          • return getPoint;
            • getPoint function object 반환
          • var obj = book();
            • return getPoint() 반환
            • getPoint function 오브젝트롤 obj에 할당
          • console.log(obj(200));
            • obj()를 호출하면 getPoint(200) 함수가 호출된다.
            • 클로저와 관련된 부분만 추려보면 아래 처리를 하게 된다.
        • 클로저와 관련된 부분
          • 실행 콘텍스트(EC) 생성
            • getPoint 오브젝트의 [[Scope]]를 외부 렉시컬 환경 참조(OLER)에 바인딩
            • 파라미터 일므에 값을 매핑하고 결과를 선언적 환경 레코드(DER)에 설정
              실행 콘텍스트(EC): {
                렉시컬 환경 컴포넌트(LEC): {
                    환경 레코드(ER): {
                        선언적 환경 레코드(DER): { param: 200 },
                        오브젝트 환경 레코드(OER): {}
                    },
                    외부 렉시컬 환경 참조(OLER): { point: 100 }
                },
                ...
              }
              
          • 함수 안의 코드 실행
          • point = point + param;
            • point를 선언적 환경 레코드에서 식별자 해결
              • point가 없으므로 외부 렉시컬 환경 참조에서 식별자 해결
              • point가 있으며 값이 100
            • param을 선언적 환경 레코드에서 식별자 해결
              • param이 있으며 값은 200
            • 100과 200을 더해 외부 렉시컬 환경 참조의 point에 할당
          • 변수가 선언적 환경 레코드에 없으면 외부 렉시컬 환경 참조에서 식별자 해결
  • 클로저와 무명 함수

    • 무명함수 안에 작성한 값, 함수는 무명함수가 끝아면 지워진다. 따라서 다시 사용하려면 저장이 필요하다. 한편, 무명함수는 저장하지 않으려는 의도로 사용
    • 클로저 활용
      • 클로저는 함수 밖 스코프의 변수와 함수를 사용할 수 있다.
      • 변수는 외부에서 직접 접근할 수 없으므로 정보를 보호
      • 무명 함수 안에서 클로저의 변수를 가진 함수를 반환하면 함수의 재사용과 정보 보호를 할 수 있다.
    • 예제
      var book = (function(){
          var point = 100;
          function getPoint(param) { return point + param };
          return getPoint;
      }());
      console.log(book(200)); // 300
      
      • function getPoint(param) {…}
        • [[Scope]]에 스코프 설정
      • return getPoint;
        • 즉시 실행 함수에서 getPoint 함수 반환하여 book 변수에 할당
        • point 변수값을 사용할 수 있다.
      • console.log(book(200));
        • 반환된 getPoint function object를 호출하면서 200을 파라미터로 넘겨준다.
      • function getPoint(param) { … }
        • getPoint function object의 [[Scope]] (외부 렉시컬 환경 참조(OLER)) 에 있는 point 100을 가져올 수 있다.
        • param은 선언적 환경 레코드(DER)에 있는 값을 가져올 수 있다.
    • Javascript에서 Closure
      • 함수에서 함수 밖의 변수 사용은 JS의 기본 메커니즘
      • 논리적 근거는 외부 렉시컬 환경 참조(OLER)에 함수가 속한 스코프가 설정되기 때문
      • 클로저는 이를 나타내는 용어로 용어보다는 논리적인 구조 이해가 필요

** 출처1. 인프런 강좌_자바스크립트 중고급: 근본 핵심 이해