Recent Posts
Recent Comments
Link
06-30 12:53
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

javascript 함수 본문

DEV&OPS/Javascript

javascript 함수

ALEPH.GEM 2022. 2. 21. 17:01

실행 가능 코드(Excutable Code)

실행 가능 코드를 만나면 Evaluation 해서 Execution Context로 만듭니다.

  • window object 에 정의 된 함수
  • 일반 함수
  • eval() : 일반적인 함수와 다르게 동적인 환경에서 실행
Execution Contexts 는 실행에 필요한 모든 정보를 관리합니다.

 

렉시컬 환경 컴포넌트(LexicalEnvironment Component)

함수 블록의 유효범위 안에 있는 식별자와 그 결과값이 저장되는 곳입니다.

식별자와 그 값을 가리키는 키쌍으로 바인드해서 렉시컬 환경 컴포넌트에 기록합니다.

환경 레코드(Environment Record)
유효 범위 안의 식별자를 기록하고 실행하는 영역. 식별자와 결과값을 바인드해서 환경 레코드에 기록합니다.
선언적 환경 레코드 : 함수, 변수등 식별자가 저장되는 영역
객체 환경 레코드 : 외부에 별도 저장된 객체의 참조
외부 렉시컬 환경 참조(Outer Lexical Environment Reference)
자바스크립트는 함수 안에 중첩해서 함수를 정의 할 수 있으므로 함수를 둘러싼 코드가 속한 렉시컬 환경 컴포넌트의 참조가 저장됩니다.

 

전역 환경(Global Environment)

브라우저 자바스크립트 실행 환경에서는 Window 객체가 전역 객체입니다.

전역 환경에는 외부 환경이 없으므로 Outer Lexical Environment Reference에는 null을 할당 합니다.

this binding component에 Window 객체의 참조가 할당되어 this가 Window 객체를 가리킵니다.

전역 변수는 전역 객체의 프로퍼티 입니다.

 

싱글 스레드

자바스크립트는 작업을 싱글 스레드로 처리합니다.

웹 브라우저의 API인 Web Workers를 사용하면 특정 작업을 백그라운드에 있는 다른 스레드에서 실행할 수 있습니다.

 

this

this는 함수가 호출되었을 때 그 함수가 속해 있던 객체의 참조이며 Execution Contexts 의 this binding component가 참조하는 객체입니다.

함수는 객체에 속해있는 것 처럼 보여도 참조하고 있을 뿐 객체 자체에 속해있지 않습니다.

  • 최상위 레벨의 this : 전역 객체, 즉 Window 객체입니다.
  • 이벤트 핸들러의 this : 이벤트가 발생한 element 객체입니다.
  • 생성자 함수 안의 this : 그 생성자로 생성한 객체입니다.
  • 생성자의 prototype 메서드 안에 있는 this : 그 생성자로 생성한 객체입니다.
  • apply와 call 메서드로 호출한 함수 안에 있는 this : 명시적으로 apply와 call 메서드를 통해 디스 바인딩 컴포넌트가 가리키는 객체를 설정할 수 있습니다.
  • 호출한 함수 안에 있는 this : 
function f(){console.log(this);}
f(); 	// 이때의 this는 최상위에 있으므로 전역 객체를 가리킴

var a = {};
a.f = f; 	//a의 property(맴버)에 위의 함수 f를 참조하여 할당
a.f(); 		//이때의 this는 함수인 객체 f를 가리킴

 

Closure : 열려 있는 것을 닫는 자료 구조

자바스크립트의 모든 함수는 클로저를 정의합니다. 

클로저 = 함수 객체 + 렉시컬 환경 컴포넌트
클로저는 함수 자신의 자유 변수의 식별자를 결정합니다.
var a = "A";
function f(){
    var b = "B";
    //속박 변수만 가지고 있는 함수는 닫힌 함수. 즉 g는 닫힌 함수
    //자유 변수를 가지고 있는 함수는 열린 함수. 즉 f는 열린 함수
    function g(){
        //속박변수: 함수의 인수와 지역 변수. 즉 c
        //자유변수: 속박변수가 아닌 변수. 즉 a와b
        var c = "c";
        console.log(a+b+c);
    }
    g();
}
f();

위 코드 전체가 g가 정의된 렉시컬 환경인데 이 렉시컬 환경 안에서 함수g의 자유 변수 a와b의 식별자 결정을 합니다.

클로저는 함수 g의 객체와 객체가 참조하는 렉시컬 환경 컴포넌트가 자유변수 a,b의 식별자를 결정하기 위한 자료구조라고 할 수 있습니다.

g의 함수 객체가 있는 한 클로저는 가비지 컬렉션 대상이 되지 않습니다.

 

클로저의 매커니즘
  1. 함수 f를 호출할 때 함수 f의 렉시컬 환경 컴포넌트가 생성됩니다.
  2. 함수 g의 함수 선언문을 eval해서 함수 객체를 생성합니다.
  3. 함수 g를 호출하면 그 시점에 g의 렉시컬 환경 컴포넌트가 생성됩니다. 이와 동시에 g의 Excution Context의 외부 렉시컬 환경 참조를 거슬러 올라가 자유 변수 a와b를 참조합니다.

 

클로저의 성질

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title> </title>
    <script>
        function makeCounter(){
            var count = 0;
            return f;
            function f() {
                return count++;
            }
        }
        var counter = makeCounter();
        console.log(counter());     /* 0 */
        console.log(counter());     /* 1 */
        console.log(counter());     /* 2 */
    </script>
</head>
<body>

</body>
</html>

위 예제는 counter()를 호출 할 때마다 지역변수 count 값을 출력 후에 1씩 증가시킵니다.

makeCounter는 f의 참조를 리턴 
f는 외부 함수인 makeCounter의 지역변수 counter를 참조
makeCounter의 실행이 끝나도 가비지 컬렉션 대상이 되지 않음
지역 변수 count는 makeCounter가 속한 렉시컬 환경 컴포넌트의 프로퍼티 이므로 클로저의 내부 상태로서 저장
count는 지역 변수이므로 함수 바깥에서 참조할 수는 없음
f는 클로저의 내부 상태를 바꾸는 메서드의 역할 

클로저는 객체와 비슷하지만 내부 상태(프로퍼티)는 외부로부터 은폐되어 숨겨져 있습니다.
즉, 클로저는 캡슐화된 객체라고 할 수 있습니다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title> </title>
    <script>
    	/* 익명 함수 버전 */
        function makeCounter(){
            var count = 0;
            return function() { return count++; }
        }
        var counter1 = makeCounter();
        var counter2 = makeCounter();
        console.log(counter1());     /* 0 */
        console.log(counter1());     /* 1 */
        console.log(counter2());     /* 0 */
        console.log(counter2());     /* 1 */
    </script>
</head>
<body>

</body>
</html>

두 개의 카운터가 별도로 카운팅 되고 있음을 알 수 있습니다.

왜냐하면 makeCounter()를 호출 할때마다 각각 makeCounter의 렉시컬 환경 컴포넌트가 새로 생성되기 때문입니다.

 

1. 외부 함수를 호출하면 그 함수의 렉시컬 환경 컴포넌트가 생성
2. 그 외부 함수 안에 속한 중첩 함수의 객체를 생성해서 리턴
→ 외부 함수의 렉시컬 환경 컴포넌트를 참조하는 중첩 함수가 정의한 클로저가 생성.
즉, 외부 함수는 클로저를 생성하는 팩토리
외부 함수가 속한 랙시컬 환경 컴포넌트는 클로저의 내부 상태 자체입니다.
외부 함수가 호출될 때마다 새로 생성됩니다. 
중첩 함수의 함수 객체가 있는 한 외부 함수가 속한 렉시컬 환경 컴포넌트는 지워지지 않으며 외부 함수의 함수 객체가 사라져도 남아 있습니다.
클로저 내부 상태는 외부로부터 은폐되어 중첩 함수 안에서만 읽거나 쓸 수 있습니다.

 

클로저를 응용한 예제

클로저를 사용하면 데이터와 그 메서드를 하나로 묶을 수 있습니다. 

마치, 자바의 메서드 처럼 객체의 멤버(프로퍼티)를 조작하는 역할을 할 수 있습니다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>객체 지향 클로저 </title>
    <script>
        function Person(name, age){
            var _name = name;
            var _age = age;
            return {
                getName: function() { return _name; },
                getAge: function() { return _age; },
                setAge: function(x) { _age = x; }
            };
        }
        var person = Person("Tom", 22);
        console.log(person.getName());      /* Tom */
        console.log(person.getAge());       /* 22 */
        person.setAge(29);
        console.log(person.getAge());       /* 29 */ 
    </script>
</head>
<body>

</body>
</html>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title> 함수 팩토리 </title>
    <script>
        function makeMulti(x){
            return function(y){
                return x*y;
            };
        }
        var multi2 = makeMulti(2);
        var multi10 = makeMulti(10);
        console.log(multi2(3));     /* 6 */
        console.log(multi10(3));    /* 30 */
    </script>
</head>
<body>

</body>
</html>

클로저를 잘못 사용한 예제

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title> 클로저 </title>
    <script>
        window.onload = function(){
            var elm = document.getElementsByTagName("input");
            /*
            //onclick 이벤트 핸들러가 바깥에 있는 변수인 i를 참조하는 클로저가 되었기 때문에
            //이벤트 핸들러가 실행되는 시점에는 for문이 끝나서 클로저가 공유하는 변수 i의 값이 3인 상태
            for(var i = 0; i < elm.length; i++){
                elm[i].onclick = function(){
                    console.log(i);
                };
            }
            */
            /* let을 이용하여 변수를 선언하면 변수 i는 블록 유효범위를 가짐 */
            /* 반복문이 실행될 때마다 새롭게 선언됨 */
            for(let i = 0; i < elm.length; i++){
                elm[i].onclick = function(){
                    console.log(i);
                };
            }
            
        };
    </script>
</head>
<body>
    <input type ="button" value = "0">
    <input type ="button" value = "1">
    <input type ="button" value = "2">
</body>
</html>

 

728x90

'DEV&OPS > Javascript' 카테고리의 다른 글

javascript ECMAScript 6부터 추가된 함수와 객체의 기능  (0) 2022.02.21
javascript 함수2  (0) 2022.02.21
javascript 브라우저 입출력  (0) 2022.02.21
javascript Expression  (0) 2022.02.21
javascript object  (0) 2022.02.21