ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • js 함수(Function)에 대한 정리( + 클로저함수, 중첩함수, 렉시컬 스코프, 콜스택, 활성 함수)
    Front/js 2024. 6. 28. 19:58

    ▤ 목차

       

       

       

      ✔ 함수

      다른 객체처럼 속성 및 method를 가질 수 있기에 일급(first-class) 객체이며 인자를 가질 수 있는 코드 블록을 말한다.

      함수는 자체 범위를 가진다.

      **부모 함수의 지역 변수에 접근할 수 있다.(closure)**

       

      ⌨ 역할

      • 호출 가능한 루틴으로서의 함수이다.
      • 값으로서 함수(인자로 전달, 변수에 의한 할당 가능, 어떤 함수의 반환값으로 사용 _ 일급객체)
      • 객체 타입으로 함수

       

      💻 특징

      • 변수 안에 담길 수 있다.
      • 객체의 속성안에 method로 담길 수 있다.
      • 다른 함수의 인자값을 전달될 수 있다.
      • 함수의 return값으로 사용할 수 있다.
      • 배열의 값으로 사용할 수 있다.

       

      👏 사용 이유

      • 코드를 재사용할 수 있다.(코드를 한번 정의하고 여러번 사용이 가능하다.)
      • 다른 인자를 사용하여 동일한 코드를 여러번 사용할 수 있고 다른 결과를 도출할 수 있다.

       

      ✏️함수 호출

      함수명(argument, argument);

       


       

       

       

      ✔사용자 정의 함수

      ⌨ 작성 방법 형식 (함수 구문)

      방법1) 함수 선언식

      function [함수이름]([매개변수1, 매개변수2,...]) {
          함수가 호출되었을 때 실행하고자 하는 실행문;
      	[return 반환값]
      }

       

      javaScript에는 호이스팅의 개념이 존재한다.

      자바스크립트는 브라우저에 내장되어 있는 엔진( chrome은 v8엔진)에 의해 해석되고 실행된다.

      호이스팅이란 인터프리터가 (var로 선언된)변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.

      var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화한다.

       

       

      방법2) 함수 리터럴(함수 표현식)

      let 변수 = function(){...}

       

      함수 선언식 함수 표현식(리터럴)
      함수는 주요 코드 흐름 중간에
      독자적인 구문 형태로 존재한다.
      함수는 표현식이나 구문 구성 내부에 생성된다.
      함수 선언문이 정의되기 전에도 호출 가능하다 실제 실행 흐름이 해당 함수에 도달했을 때 함수를 생성한다.
      따라서 실행 흐름이 함수에 도달했을때부터
      해당 함수를 사용할 수 있다. 

       

       

      방법3) 함수 생성자 사용

      let 변수 = new Function(){...}

       

       

      💻 코드로 보기

      let count = 0;
      function aa(){ //선언
      	count +=1;
      	document.write(count + "번 수행<br>");
      }
      
      aa(); // 함수 호출
      aa();

       

      🔶렉시컬 스코프

      함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 상위 스코프가 결정된다.

      선언한 시점에 상위 스코프가 결정된다.

      즉, 함수를 어디서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다.

       

      아래의 코드를 보고 결과를 예측해보자.

      <script>
          var a = 5;
          function Func1(){
              var a = 10;
              Func2();
          }
      	//함수 선언식
          function Func2(){
              document.write(a);
          }
      
          Func1();  //?
          document.write(`<br>`);
          Func2();  //?
      </script>

       

      Func1()은 10 , Func2()는 5를 예측했다면 렉시컬 스코프를 이해하고 있지 않는 것이다.

      Func2 함수의 상위 스코프가 무엇인지에 따라 결과는 달라질 것이다.

      상위 스코프를 결정하는 기준은 무엇일까?

       

      1. 함수를 어디서 호출하였는가?

      2. 함수를 어디서 선언하였는가?

      이렇게 2가지로 나뉠 것이다. 

       

      1번 방식(호출 위치)으로 상위 스코프를 결정한다면 함수 Func2의 상위 스코프는 Func1과 전역일것이다.

      2번 방식(선언 위치)으로 상위 스코프를 결정한다면 상위 스코프는 전역일 것이다.

       

      호출 위치에 따라 상위 스코프를 결정하는 것을 동적(Dynamic) 스코프라고 한다.

      이는 bach Scripting, Common Lisp 등 일부 언어에서 따른다.

       

      선언 위치에 따라 상위 스코프를 결정하는 방식을 렉시컬 스코프 혹은 정적 스코프라고 한다.

      대부분의 프로그래밍 언어는 렉시컬 스코프 방식을 사용한다.

       

      위의 코드를 다시 확인해보자. 

      Func2의 선언 위치는 전역이다. 즉, Func2의 상위 스코프는 전역 스코프이다.

      결과는 아래와같이 동일하게 5가 찍히게 된다. 

       


       

       

       

      즉시 실행 함수(IIFE)인 익명 함수

       

      ⌨ 형식

      즉시 실행 함수(IIFE)인 익명 함수가 있다.

      선언이 되자마자 호출되는 함수 표현식이다. 일회용이며 휘발성이 있다.

      let myfunc = function(){
      	document.write(`<br>함수 표현식 : 익명 함수 실행(휘발성, 동적)`);
      }
      myfunc();

       

      function을 이름이 없이 선언하는 것이다.

      무명 함수는 파싱할때 미리 메모리를 생성하지 않는다. 즉, 호이스팅을 하지 않는 함수이다.

      선언한 곳에서 생성되어 실행 후 메모리를 해제한다. (동적으로 메모리를 확보한다.)

      코드를 한번 사용하는 경우 무명 함수를 사용해라. 메모리 절감 효과가 있다.

      Uncaught ReferenceError: Cannot access 'myfunc' before initialization 
      // 무명 함수위에서 선언한 경우, 위와 같은 에러가 발생한다.

       

       

      💻 익명 함수 반환

      방법 1) 

      function outer1(){
      	return function(){
      		document.write(`<br>Hellw World`);
      	}();
      }
      outer1();

       

       

      방법 2) 반환 함수

      function outer2(name){
      	let msg = "안녕 내친구 " + name;
      	return function(){
      		document.write(`<br>${msg}`);
      	}
      }
      outer2("홍길동")();

       

       

      방법3) 파라미터가 있는 경우

      let mbc = function(para1){
      	let b = function(para2){
      		return para2 *2;
      	}
      	return `결과 : ${b(para1)}`;
      };
      
      //b(5); 오류발생
      document.write(`<br>`+mbc(11));

       

       


       

       

       

      ✔ 중첩 함수

       

      ⌨코드로 보기

      function func5(){
      	function fu1(){
      		document.write(`<br>fu1 수행`);
      	};
      	function fu2(){
      		document.write(`<br>fu2 처리`);
      	}
      	fu1();
      	fu2();
      }
      func5();

       

      충첩된 함수이다.

      함수도 지역 함수와 전역함수로 구분된다.

       

      즉 아래와 같이 지역 함수를 전역에서 호출하면 에러가 발생한다.

      document.write(`<hr>중첩 함수(내부 함수)--`);
      function func5(){
      	function fu1(){
      		document.write(`<br>fu1 수행`);
      	};
      	function fu2(){
      		document.write(`<br>fu2 처리`);
      	}
      	fu1();
      	fu2();
      }
      fu1(); //지역 함수이다.

       

      함수안에 다른 함수가 중첩되어 있다. 내부 함수의 실행 컨텍스트는 외부 함수의 실행이 끝난 후에 스택에서 제거된다.

       

       

      🤔 클로저(cosure) 함수? 

       

      클로저는 반환된 내부 함수가 자신이 선언되었을 당시 스코프를 기억하여 자신이 선언되었을 때 환경 밖에서 호출이 되어도 해당(스코프)에 접근할 수 있는 함수를 말한다.

      + 선언을 위치 기준으로 스코프가 결정되는 개념은 위에서 렉시컬 스코프라고 정리했다.

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

       

      <script>
          // 함수 표현식 사용
           const outerF = function(){
              let a = 10; //자유변수(Free variable)
              //클로저
              const innerF = function(para){
                  a = a * para;
                  document.write(a);
              }
              return innerF;
          }
      
          const mulFunc = outerF();
          
          mulFunc(5); //?
          document.write(`<br>`);
          outerF()(3); //?
      </script>

       

      렉시컬 스코프에 의해 선언된 위치에서 지역 변수(우선)를 찾고 없으면 상위 스코프에서 해당 변수를 참조한다. 

      실행 결과를 보자.

       

       

       

       

       

      const mulFunc = outerF(); 부분의 코드를 보자.

      outerF 함수는 내부함수(innerF)를 반환하면서 죽었다.

      다시말해, outerF(); 함수가 호출된 후 *콜스택(실행 컨텍스트 스택)에서 제거되기 때문에 outerF가 가지고 있는 a 변수 또한 유효하지 않게 보인다.

      결과에서 확인했듯 a에 10 값은 여전히 유효하다.

      여기서 클로저(Closure)의 개념이 등장한다.

      자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부 함수가 호출되더라도 외부함수의 지역함수의 지역 변수에 접근할 수 있도록 하는 함수를 Closure라고 부른다.

      (외부 함수의 실행 컨텍스트가 제거 되더라도 내부 함수의 실행 컨텍스트가 스택에 남아 있다.)

       

      클로저에 의해 참조되고 있는 변수들은 스택이 아닌 힙 영역에 저장된다.

      클로저에 의해 참조되는 변수들은 함수 실행이 끝나도 메모리에 유지된다. 클로저는 해당 변수들을 계속 사용할 수 있기때문이다.

       

       

      ✒️콜스택, 활성객체, 실행 콘텍스

      [콜스택?]

      더보기

      스택(Stack)이란?

      자료 구조 중 하나로 LIFO(선입선출) 방식으로 작동한다.

       

      *콜스택(함수의 재귀호출)

      위의 스택의 사용 중 하나이다.

      자바 스크립트는 (힙, 큐와 함께 구성) 하나의 스레드로 1개의 동시성만 다루는 언어이다. (한번에 1개의 작업만 수행)

       

      자바스크립트 엔진(chrome은 v8)이 구동되면서 실행중인 코드를 추적하는(기록) 공간이 있는데, 이 공간을 콜스택이라고 한다. 즉, 작업을 하면서 과정을 순서대로 쌓아두는 것과 비슷하다.

       

      위의 코드로 보자면

      mulFunc 함수는 outerF함수 호출

      outerF 함수는 innerF함수 호출

      아래와 같이 스택이쌓이게 되는 것이다. 

       

      |                 |

      innerF     |

      outerF     |

      mulFunc  |

       

      이 경우, 모든 콜스택이 push되고 문제가 없는 경우 상단의 함수부터 하나씩 pop하면서 콜스택을 비우는 작업을 한다. 이렇게 콜스택이 모두 제거되면 프로그램이 종료된다. (중간에 오류가 발생하면 비정상 종료가 된다.)

      [활성객체?]

      더보기

      활성 객체는 함수가 실행될 때 생성되는 객체를 말한다.

      객체 안에는 함수가 실행되는 동안 필요한 변수들과 함수의 매개변수(parameter) 등이 저장된다.

      활성 객체는 해당 함수의 스코프(범위)안에 있는 변수와 함수에 접근할 때 사용된다.

       

      예를 들어, 부자집이라고 생각하자.

      누군가는 주방에서 요리를 하고 또 다른 누군가는 방을 청소하는 등 해당 공간에서 활동하는 사람들이 있다고 생각해보자. 활성객체는 프로그램 안에서 일하는 공간을 말한다.

      또 그 각자가 맡은 역할이 있는 것이다.

      [실행 컨텍스트?]

      더보기

      실행 컨텍스트(Execution Context)는 코드가 실행될 때 생성되는 환경을 말한다.

      현재 실행되는 코드와 관련된 여러 정보(변수, 함수 선언, this .. 등)들이 저장된다.

      실행 컨텍스트는 Stack에 쌓여 실행된다.

      함수가 호출될 때마다 새로운 실행 컨텍스트가 생성되어 스택에 쌓이게된다.

       


       

       

      😊정리

       

      활성 객체함수가 실행될 때 생성되는 객체이다.

      실행 컨텍스트코드 실행 환경을 관리하는데 사용된다.

      콜스택함수 호출과 반환을 관리하는 자료구조를 말한다. 

       

      함수안에 변수가 있는 경우, 변수들은 보통 함수가 호출될 때 생성되거 함수가 끝나면 사라진다.

      그런데 때로는 함수 안에 있는 변수를 함수 밖에서도 사용하고 싶은 경우가 있다.

      이때, 클로저의 개념이 나온다. 클로저는 함수 안에 있는 변수를 함수 밖에서 사용할 수 있게 해준다.

      클로저가 사용하는 변수들은 힙(heap)영역에 저장되고 그 함수의 실행 컨텍스트는 스택에 쌓인다.

      클로저는 함수가 끝나고도 변수들을 기억하고 유지할 수 있게 된다.

      (즉, 클로저를 사용하면 외부함수가 끝나더라도 GC가 메모미를 해제해주지 않는다. 직접 반납해야한다.)

       

      클로저의 활용 참고하기 좋은 블로그

      https://velog.io/@chojs28/%EB%A0%89%EC%8B%9C%EC%BB%AC-%EC%8A%A4%EC%BD%94%ED%94%84%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%BB%A4%EB%A7%81

       

      렉시컬 스코프와 클로저, 그리고 커링

      MDN에 클로저의 정의는 ‘함수와 함수가 선언된 어휘적 환경의 조합이다’ 라고 나타나 있다.그리고 클로저를 이해하려면 렉시컬 스코프(Lexical Scope)를 먼저 이해해야 한다고 나와있다.따라서,

      velog.io

      https://poiemaweb.com/js-closure

       

      Closure | PoiemaWeb

      클로저(closure)는 자바스크립트에서 중요한 개념 중 하나로 자바스크립트에 관심을 가지고 있다면 한번쯤은 들어보았을 내용이다. execution context에 대한 사전 지식이 있으면 이해하기 어렵지 않

      poiemaweb.com

       

    Designed by Tistory.