반응형

클로저 이해하기

클로저의 기본적인 예

 

<script>
    // 클로저의 기본적인 예
    function sum(base) {
        var inClosure = base;

        return function(adder) {
            return inClosure + adder;
        };
    };
    
    var fiveAdder = sum(5); // 5 and return function
    console.log(fiveAdder.toString());
    fiveAdder(3); // inClsure(5) + adder(3) = 8
    var threeAdder = sum(3); // 3 and return function
    console.log(threeAdder.toString());
</script>

 

위 소스에서도 함수 안에 다른 함수를 선언하고 외부에서 내부 함수를 사용하는 클로저의 특징을 확인할 수 있다.

외부에서 sum()함수를 호출하면 파라미터 base를 통해 넘어온 값은 inClosure변수에 저장된다. 그리고 내부 함수에서 inClosure변수를 참조한다.

 

var fiveAdder = sum(5);

 

선언되는 부분부터 보면, var fiveAdder를 통해 sum()함수가 호출되고 sum()함수의 파라미터인 base는 5로 넘어와서 inClsure변수도 5로 설정된다. 그리고 inClosure변수를 참조하는 내부 함수를 반환하여 fiveAdder에 저장한다.

이제부터 fiveAdder를 통해 함수를 호출하게 되면 스코프 체인을 따르게 된다.

그리고 나중에 fiveAdder를 통해 함수를 호출하게 되면 fiverAdder가 레퍼런스를 가지고 스코프 체인을 사용하게 되는 것이다.

 

fiveAdder(3);

 

이제 fiveAdder(3)이 호출되면, 위에 스코프 체인에서 inClosure는 5를 갖고, 내부함수의 파라미터 adder로 들어온 값에 3을 더하여 8이라는 값이 계산된다.

 

var threeAdder = sum(3);

 

다음으로 threeAdder를 호출하면 새로운 스코프 체인을 생성한다.

 

이처럼 같은 함수를 통해 받은 값은 각각의 함수가 생성되어, fiveAdder, threeAdder로 호출하는 스코프 체인이 만들어진다.

 

이처럼 클로저를 통해 각 함수는 자기만의 고유한 값을 보유하고 스코프 체인을 유지하면서 그 체인 안에 있는 모든 변수의 값들을 유지한다.

 

한가지 추가해보면, fiveAdder와 threeAdder변수의 형태는 아래처럼 서로 같다.

 

 

하지만 형태 이외에, fiveAdder !== threeAdder로 두개의 변수는 같지 않다. sum()함수를 호출할 때마다 같은 모양의 함수들이 새롭게 나오지만, 두 함수가 할당받은 스코프 체인, 숨겨져 있는 클로저가 다르기 때문이다.

 

 

다음은 setInterval()함수를 통한 클로저의 발생이다.

<button id="btnToggle">Toggle</button>
<div id="divPending">Pending</div>

<script>
    // setInterval() 함수를 통한 클로저 발생
    (function() {
        var pendingInterval = false,
        div = document.getElementById("divPending"),
        btn = document.getElementById("btnToggle");

        function startPending() {
            if(div.innerHTML.length > 13) {
                div.innerHTML = "Pending";
            }

            div.innerHTML += ".";
        };
        btn.addEventListener("click", function() {
            if(!pendingInterval) {
                pendingInterval = setInterval(startPending, 500);
            } else {
                clearInterval(pendingInterval);
                pendingInterval = false;
            }
        });
    }());
</script>

 

위 소스를 실행하면 아래와 같은 화면이 나온다. Toggle버튼을 클릭하면 “Pending”뒤에 “.”을 하나씩 추가하다가 일정 개수가 넘으면 다시 초기화하는 방식으로 동작한다.

 

Toggle을 클릭했을 때

 

 

var pendingInterval = false,
div = document.getElementById("divPending"),
btn = document.getElementById("btnToggle");

 

위 변수들은 내부에서만 접근 할 수 있도록 private변수로 선언했다. 그리고 startPending()함수 안에 있을 법한 divPending을 가져오는 부분을 상위 스코프에 미리 가져다 놓음으로써, 매번 <div>를 getElementById로 탐색해서 가져오지 않도록 했다.

 

아래는 클로저가 발생한 부분들이다.

 

if(div.innerHTML.length > 13) {
   div.innerHTML = "Pending";
}

 

if문에서 상위 스코프 div변수를 참조하는 부분과

 

pendingInterval = setInterval(startPending, 500);

 

setInterval()함수에서 첫번째 인자로 startPending함수를 사용하는 부분이다.

이런 경우를 보면, 함수 안에 함수가 있어서 내부에 있는 함수가 반환되지 않고 이벤트 콜백함수로 호출될 때도 클로저가 발생하는 것을 알 수 있다.

 

 

반응형
반응형

클로저(Closure)란 ?

클로저는 특정 함수가 참조하는 변수들이 선언된 렉시컬 스코프(Lexical Scope)는 계속 유지되는데, 그 함수와 스코프를 묶어서 클로저라 한다.

 

:: 렉시컬 스코프(Lexical Scope) : 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정하는 것이다. 중요한 점은 함수의 호출이 아닌 함수의 선언에 따라 결정된다는 점이다.

 

<script>

   var num = 1;
   function a() {
       var num = 10;
       b();
   }
 
   function b() {
       console.log(`num : ${num}`);
   }
 
   a(); // 1
   b(); // 1

</scrip>

 

이렇게 출력되는 이유는 함수의 호출로 상위 스코프가 결정된 것이 아니라 함수의 선언에 따라 상위 스코프가 결정되었기 때문이다. 즉 a, b모두 1을 출력한 것으로 볼 때 b함수가 전역을 가리키고 있다는 것을 알 수 있다.

 

<script>

   var name = 'Hello World'; // 전역변수
 
   function c() {
       var name = 'New World'; // 지역변수
       // secondName()은 내부함수이며, 클로저이다.
       function secondName() {
           // 부모함수에서 선언된 변수를 사용한다.
           console.log(`name : ${name}`); // New World
       }
       secondName();
   }
   
   c();

</script>

 

c();는 지역변수 name, secondName()함수를 생성한다. secondName()함수는 c()안에 정의된 내부 함수이며, c() 함수 내부에서만 사용이 가능하다.

 


 

최종정리

 

<script>
 
   // 클로저의 예
   function outer() {
       var count = 0;
       var inner = function() {
           return ++count;
       };
       return inner;
   }
   var increase = outer();
 
   console.log(increase()); // 1
   console.log(increase()); // 2
 
</script>

 

:: 정리

1. count변수는 outer()함수의 로컬 변수다.따라서 원칙적으로는 outer()함수 내부에서만 접근이 가능하다.

2. outer()함수 내부에 다시 함수를 하나 선언하여 inner변수에 할당했다.

3. inner변수에 할당한 함수는 outer()함수의 로컬 변수인 count에 접근하여 1만큼 증가시키고 이 값을 반환한다.

4. outer()함수의 반환값으로 inner변수를 지정하면서 함수 정의를 마친다.

 

반응형
반응형

각각의 div를 클릭했을 때 어떤 div를 클릭했는지 알려주는 소스

 

문제점

 

<div id="div0">Click0</div>
<div id="div1">Click1</div>
<div id="div2">Click2</div>

<script>

    var len = 3;

    // i는 모두 3이 출력된다.
    for(var i = 0; i < len; i++) {
        document.getElementById("div" + i).addEventListener("click", function() {
            alert("You Clicked div #" + i);
            console.log(i);
        }, false);
    }
    
</script>

 

문제점 : Click0, Click1, Click2중 어떤것을 클릭해도 "You Clicked div #3이 나오는 현상

 

이유 : 9번째줄에 콜백 함수는 5번줄에 선언된 변수에 접근할 수 있는 스코프를 생성하게 된다.

이후 div에 클릭 이벤트가 발생해서 콜백 함수가 호출될 때도 클릭에 설정한 이벤트 핸들러의 콜백 함수는 5번 줄의 변수들에 계속 접근할 수 있는 스코프를 갖게된다.

이것은 for-loop를 통해서 각각 div에 순서대로 클릭 이벤트 핸들러를 부여할 때 i가 0~3까지 증가한 뒤, 이후 for-loop가 끝나고 나서도 계속 유지된다. 따라서 나중에 10번줄의 alert()함수가 호출될 때 변수 i의 값은 이미 for-loop가 끝난 후의 값인 3으로 출력된다.

for-loop가 돌 때는 별도의 스코프가 생성되지 않고 i는 글로벌 스코프에 존재한다.

그러다 addEventListener()로 콜백 함수를 설정할 때 익명 함수가 선언되면서 스코프가 생성되어 스코프 체인을 만들게 된다.

 

function을 이용한 문제해결

 

<div id="div0">Click</div>
<div id="div1">Click</div>
<div id="div2">Click</div>

<script>

    var len = 3;
    
    // function을 이용한 문제해결
    function setDivClick(index) {
        document.getElementById("div" + index).addEventListener("click", function() {
            console.log(index);
            alert("You Clicked div #" + index);
        }, false);
    }

    // var i, len = 3;
    for(var i = 0; i < len; i++) {
        setDivClick(i);
        console.log(i);
    }

</script>

 

closure를 활용한 문제해결

 

<div id="div0">Click</div>
<div id="div1">Click</div>
<div id="div2">Click</div>

<script>

    // closure를 활용한 문제해결
    for(var i = 0; i < len; i++) {
        document.getElementById("div" + i).addEventListener("click", (function(index) {
            return function() {
                alert("You Clicked div#" + index);
            };
        }(i)), false);
    }

</script>

 

반응형
반응형

예외처리란 ?

프로그램이 실행되는 동안 문제가 발생하면 프로그램이 자동으로 중단된다. 이때 프로그램이 대처할 수 있게 처리하는 것을 예외 처리라고 한다.

 

- 프로그램 실행 중 발생하는 오류 : 예외(Exception)

- 프로그래밍 언어의 문법적인 오류로 인해 프로그램이 실행되기 전에 발생하는 오류 : 에러(error)

 

try catch finally

 

try {

} catch() {

} finally {

}

 

try 구문 안에서 예외가 발생하면 이를 catch 구문에서 처리한다. finally 구문은 필수 사항은 아니며, 예외 발생 여부와 상관없이 수행해야 하는 작업이 있을 때 사용한다.

 

:: try catch 예제

willExcept 자체가 존재하지 않는데, willExcept의 byeBye() 메서드를 사용한다. willExcept 객체도 없고 byeBye() 메서드도 존재하지 않는다. ( 예외처리를 알아보기 위한 강제 예외 )

 

<script>
    try {
        willExcept.byeBye();
    } catch(exception) {

    }

</script>

 

try 구문 안에서 예외가 발생하면 더 이상 try 구문을 진행하지 않고 catch 구문을 실행시킨다.

 

:: catch구문

아래 코드는 willExcept.byeBye()를 실행하려는 순간에 예외가 발생해 catch 구문을 실행한다.

그러므로 alert('try 구문 종료'); 는 실행되지 않고 '예외처리'만 실행된다.

 

<script>
    try {
        willExcept.byeBye();
        alert('try 구문 종료');
    } catch(exception) {
    	alert('예외처리');
    }
</script>

 

:: finally구문

 

<script>
    try {
        willExcept.byeBye();
    } catch(exception) {
    	alert('예외처리');
    } finally {
    	alert('무조건 발생');
    }
</script>

 

try에서 예외가 발생하고, catch 구문이 실행된다. try에서 예외가 발생을 하거나 말거나 상관없이 finally 구문은 실행된다.

그러므로 '예외처리', '무조건 발생' 두가지 다 실행된다.

반응형
반응형
<input type="text" id="input_event" />


<script>
        input_event.addEventListener('keydown', () => {
            console.log('keyDown');
        });

        input_event.addEventListener('keyup', () => {
            console.log('keyUp');
        });

        input_event.addEventListener('keypress', () => {
            console.log('keyPress');
        });
</script>

 

1. keydown

 - 키보드를 눌렀을때 가장 먼저 실행된다.

 - 키보드를 길게 누르고 있으면 연속적으로 실행된다.

 

2. keypress

 - 키보드를 눌렀을때 두번째로 실행된다.

 - 키보드를 누르고 있으면 연속적으로 실행된다.

 

3. keyup

 - 키보드에서 손을 뗄 때 한번만 실행된다.

 - 키보드를 누르고 있을때는 실행되지 않는다.

 

:: ctrl, alt, shift, esc키 등은 keypress에서는 작동하지 않으며, keydown과 keyup에서만 작동한다.

 

:: keyCode값

: keydown, keyup에서 q = 81이며, keypress에서 q = 113번으로 번호가 다르다.

: 하지만 대문자로 쓰면 번호가 같아진다. keydown, keyup, keypress에서 Q = 81

 

:: **keydown과 keypress는 이전에 누른 값을 가지고 있으며, keyup은 현재 누른 값을 가지고 있다.


:: (Error) FireFox에서 keyCode가 먹히지 않는 부분

    <input type="text" id="input_event" />

    <script>
        input_event.addEventListener('keydown', () => {
            // Chrome
            console.log(event.keyCode);
            
            // FireFox
            console.log(event.which);
        });

        input_event.addEventListener('keyup', () => {
            // Chrome
            console.log(event.keyCode);
            
            // FireFox
            console.log(event.which);
        });

        input_event.addEventListener('keypress', () => {
            // Chrome
            console.log(event.keyCode);
            
            // FireFox
            console.log(event.which);
        });
    </script>

 

3항 연산자를 사용할 경우

 

let code = event.keyCode ? event.keyCode : event.which;

 

if문을 사용할 경우

 

let code;

if(event.keyCode === undefined || event.keyCode === null) {
    code = event.which;
} else {
    code = event.keyCode;
}

 

반응형
반응형

BODY 구성

<body>
    <h1>Header 1</h1>
    <h1 id="target">Header 2</h1>
    <h1>Header 3</h1>
    
    <script>
        $(document).ready(function() {
            $('#target').css('color', 'orange');
        });
    </script>
</body>

HTML웹 표전에 따르면 id속성은 HTML페이지 내에서 유일한 값을 가져야 한다. 즉, 위 구성에서 id속성으로 target은 무조건 한개만 존재해야 합니다. ( 실제 실행에서는 2개 이상이어도 문제는 없습니다. )

 

h1태그 중 id속성이 target인 문서 객체 선택하기

<script>
    $(document).ready(function() {
        $('h1#target').css('color', 'orange');
    });
</script>

클래스 선택자

<body>
    <h1 class="item">Header</h1>
    <h1 class="item select">Header 1</h1>
    <h1 class="item">Header 2</h1>
</body>

<script>
    $(document).ready(function() {
        $('.item').css('color', orange');
        $('h1.item').css('background', 'red');
    });
</script>

두 class속성이 모두 있는 문서 객체만 적용하기

<script>
    $(document).ready(function() {
        $('.item.select').css('color', 'orange');
    });
</script>

현재 body태그에서 두 class속성이 모두 있는 문서 객체는 두번째 위치한 h1태그 이므로, 두번째 h1태그에만 스타일이 적용이 됩니다.

반응형
반응형

1. 전개 연산자를 활용한 배열 합치기 ( Spread Operator(...) )

전개 연산자를 사용한 배열 병합

<script>
    const arrayA = [1, 2, 3, 4, 5];
    const arrayB = [52, 273, 99, 100, 101];
    
    const newArray = [...arrayA, ...arrayB];
    
    console.log(newArray); // [ 1, 2, 3, 4, 5, 52, 273, 99, 100, 101 ]
    
</script>

 

전개 연사자를 사용한 배열 병합 2

<script>
    const originalArray = [52, 273, 55, 45];
    
    const newArrayA = [1, 2, 3, 4, 5, ...originalArray];
    const newArrayB = [...originalArray, 1, 2, 3, 4, 5];
    
    console.log(newArrayA); // [ 1, 2, 3, 4, 5, 52, 273, 55, 45 ]
    console.log(newArrayB); // [ 52, 273, 55, 45, 1, 2, 3, 4, 5 ]
    
</script>

...originalArray처럼 전개연산자는 originalArray의 원소들을 쪼개어 개별요소로 리턴합니다.

 

2. .push()를 사용한 배열 합치기

<script>
    const array1 = [1, 2, 3, 4, 5];
    const array2 = [6, 7, 8, 9, 0];
    
    array1.push(array2);
    
    console.log(array1.length); // 6
    console.log(array1[5]); // [ 6, 7, 8, 9, 0 ]

</script>

.psh()를 사용하여 배열을 합치면 파라미터로 전달된 배열을 하나의 원소로 처리가 됩니다.

그러므로, 합쳐진 array1의 length는 10이 아닌 6이 되고, array1[5]의 원소는 [ 6, 7, 8, 9, 0 ]이 출력됩니다.

 

3. 전개 연산자와 .push()를 활용한 배열 합치기

<script>
    const array1 = [1, 2, 3, 4, 5];
    const array2 = [6, 7, 8, 9, 0];
    
    array1.push(...array2);
    
    console.log(array1.length); // 10
    console.log(array1[5]); // 6

</script>

전개 연사자는 배열의 원소들을 쪼개어 개별요소로 리턴한다고 했습니다.

그렇기 때문에 2번처럼 하나의 원소가 아닌 개별원소로 처리가 됩니다.

그러므로, array1의 length는 10이 되며, array1[5]의 원소는 6이 출력됩니다.

 

4. .concat()을 사용한 배열 합치기

<script>
    const array1 = [1, 2, 3];
    const newArray = array1.concat('a', 'b', ['c', 'd'], 'e');
    
    console.log(newArray.length); // 8
    console.log(newArray); // [ 1, 2, 3, 'a', 'b', 'c', 'd', 'e' ]
    
</script>

.concat()은 array1과 전달받은 파라미터들을 합쳐서 새로운 배열을 생성하여 리턴합니다.

이 때, 파라미터가 배열일 경우 배열 안의 원소들을 뽑아서 새로운 배열에 추가합니다.

그러므로, [ 'c', 'd' ] 배열이 하나의 원소로 취급되지 않아서 newArray.length가 7이 아닌 8이 됩니다.

 

반응형

+ Recent posts