setTimeout을 서버에 요청을 하고 바로 func3를 실행하기에 func1, func3, func2순서로 나온다. setTimeout은 모든 동기식이 실행된 후에 실행된다.
3. 이벤트 루프(Event Loop)와 동시성(Concurrency)
브라우저는 단일 쓰레드(single-thread)에서 이벤트 드리븐(event-driven) 방식으로 동작한다.
자바스크립트의 동시성(Concurrency)을 지원하는 것이 바로 이벤트 루프(Event Loop)이다.
js는 비동기를 못하지만 그것을 browser가 한다.
thread란 컴퓨터가 일을 할때 한번에 일을 할 수 있는 정도를 나타낸다.
이벤트 드리븐(event-driven) 방식이란? 모든 application이 이벤트 방식으로 동작한다.
이벤트루프와 브라우저의 환경
Web API는 browser의 영역으로써 Ajax, DOM Events, Timer등이 있다.
구글의 V8을 비롯한 대부분의 자바스크립트 엔진은 크게 2개의 영역으로 나뉜다.
Call Stack(호출 스택) == 실행 컨텍스트
작업이 요청되면(함수가 실행되면) 요청된 작업은 순차적으로 Call Stack에 쌓이게 되고 순차적으로 실행된다. 자바스크립트는 단 하나의 Call Stack을 사용하기 때문에 해당 task가 종료하기 전까지는 다른 어떤 task도 수행될 수 없다.
Heap
동적으로 생성된 객체 인스턴스가 할당되는 영역이다.
자바스크립트 엔진은 단순히 작업이 요청되면 요청된 작업을 Call Stack을 사용하여 순차적으로 실행할 뿐이다. 앞에서 언급한 동시성(Concurrency)을 지원하기 위해 필요한 비동기 요청(이벤트를 포함) 처리는 자바스크립트 엔진을 구동하는 환경 즉 브라우저(또는 Node.js)가 담당한다.
Event Queue(Task Queue)
비동기 처리 함수의 콜백 함수, 비동기식 이벤트 핸들러, Timer 함수(setTimeout(), setInterval())가 보관되는 영역으로 이벤트 루프(Event Loop)에 의해 특정 시점(Call Stack이 비어졌을 때)에 순차적으로 Call Stack으로 이동되어 실행된다.
Event Loop(이벤트 루프)
Call Stack내에서 현재 실행중인 task가 있는지 그리고 Event Queue에 task가 있는지 반복하여 확인한다.
4. 이벤트의 종류
4.1 UI Event
웹페이지가 로드될때
load 웹페이지의 로드가 완료되었을 때
unload 웹페이지가 언로드될 때(주로 새로운 페이지를 요청한 경우)
error 브라우저가 자바스크립트 오류를 만났거나 요청한 자원이 존재하지 않는 경우
resize 브라우저 창의 크기를 조절했을 때
scroll 사용자가 페이지를 위아래로 스크롤할 때
select 텍스트를 선택했을 때
script문을 제일 하단에 써줘야하는 이유는 html이 전부다가 load되면 js를 실행하게 하기위해서이다.
4.2 Keyboard Event
keydown 키를 누르고 있을 때
keyup 누르고 있던 키를 뗄 때
keypress 키를 누르고 뗏을 때
4.3 Mouse Event
click 마우스 버튼을 클릭했을 때
dbclick 마우스 버튼을 더블 클릭했을 때
mousedown 마우스 버튼을 누르고 있을 때
mouseup 누르고 있던 마우스 버튼을 뗄 때
mousemove 마우스를 움직일 때 (터치스크린에서 동작하지 않는다)
mouseover 마우스를 요소 위로 움직였를 때 (터치스크린에서 동작하지 않는다) // hover와 비슷
mouseout 마우스를 요소 밖으로 움직였를 때 (터치스크린에서 동작하지 않는다)
4.4 Focus Event
focus/focusin 요소가 포커스를 얻었을 때
blur/foucusout 요소가 포커스를 잃었을 때
4.5 Form Event
input input 또는 textarea 요소의 값이 변경되었을 때, contenteditable 어트리뷰트를 가진 요소의 값이 변경되었을 때
change select box, checkbox, radio button의 상태가 변경되었을 때
<!DOCTYPE html> <html> <body> <labelfor="username">User name </label> <inputtype="text"id="username"> <emid="message"></em> <script> var elem = document.getElementById('username'); var msg = document.getElementById('message');
elem.addEventListener('blur', function () { if (elem.value.length < 2) { msg.innerHTML = '이름은 2자 이상 입력해 주세요'; } else { msg.innerHTML = ''; } }); </script> </body> </html>
value(사용자에 의해 입력된 값)을 가져온다.
2자 이상이라는 규칙을 상수화하고 함수의 인수로 전달도록 수정하자. 이렇게 하면 규칙이 변경되어도 함수는 수정하지 않아도 된다.
1 2 3 4 5
functionfoo() { alert('clicked!'); } // elem.addEventListener('click', foo()); // 이벤트 발생 시까지 대기하지 않고 바로 실행된다 elem.addEventListener('click', foo); // 이벤트 발생 시까지 대기한다
// elem.addEventListener(‘click’, foo()); // 이벤트 발생 시까지 대기하지 않고 바로 실행된다. elem.addEventListener('click', foo); // 이벤트 발생 시까지 대기한다 이것을 사용해야하며 헷갈리지 않도록 조심해야한다.
문제점으로는 인수를 전달할 수 없는 문제가 발생한다. 아래의 방법으로 우회하며 중요하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
var MIN_USER_NAME_LENGTH = 2; // 이름 최소 길이
var elem = document.getElementById('username'); var msg = document.getElementById('message');
<!DOCTYPE html> <html> <head> <style> html, body { height: 100%; } </style> <body> <p>A paragraph with a <button>button</button>.</p> <script> var body = document.querySelector('body'); var para = document.querySelector('p'); var button = document.querySelector('button');
// 버블링 body.addEventListener('mousedown', function() { console.log('Handler for body.'); });
// 버블링 para.addEventListener('mousedown', function() { console.log('Handler for paragraph.'); });
<!DOCTYPE html> <html> <head> <style> html, body { height: 100%; } </style> <body> <p>A paragraph with a <button>button</button>.</p> <script> var body = document.querySelector('body'); var para = document.querySelector('p'); var button = document.querySelector('button');
// 캡처링 body.addEventListener('mousedown', function() { console.log('Handler for body.'); }, true);
// 캡처링 para.addEventListener('mousedown', function() { console.log('Handler for paragraph.'); }, true);
<!DOCTYPE html> <html> <head> <style> html, body { height: 100%; } </style> <body> <p>A paragraph with a <button>button</button>.</p> <script> var body = document.querySelector('body'); var para = document.querySelector('p'); var button = document.querySelector('button');
// 버블링 body.addEventListener('mousedown', function() { console.log('Handler for body.'); });
// 캡처링 para.addEventListener('mousedown', function() { console.log('Handler for paragraph.'); }, true);
<!DOCTYPE html> <html> <head> <style> html, body { height: 100%; } div { height: 100%; } </style> </head> <body> <div> <button>배경색 변경</button> </div> <script> functionbluify(e) { // this: 이벤트 리스너에 바인딩된 요소(div 요소) console.log('this: ', this); // target: 이벤트를 발생시킨 요소(button 요소 또는 div 요소) console.log('e.target:', e.target); // currentTarget: 이벤트 리스너에 바인딩된 요소(div 요소) console.log('e.currentTarget: ', e.currentTarget);
// 언제나 true console.log(this === e.currentTarget); // currentTarget과 target이 같은 객체일 때 true console.log(this === e.target);
// click 이벤트가 발생하면 이벤트를 발생시킨 요소(target)과는 상관없이 this(이벤트 리스너에 바인딩된 요소(div 요소))의 배경색이 변경된다. this.style.backgroundColor = '#A5D9F3'; } // div 요소에 이벤트 핸들러가 바인딩되어 있다. // 자식 요소인 button이 발생시킨 이벤트가 버블링되어 div 요소에도 전파된다. // 따라서 div 요소에 이벤트 핸들러가 바인딩되어 있으면 자식 요소인 button이 발생시킨 이벤트를 div 요소에서도 핸들링할 수 있다. document.querySelector('div').addEventListener('click', bluify); </script> </body> </html>
div를 클릭하였을때 event가 발생하면 target과 this(current.target)은 div로 같다.
<!DOCTYPE html> <html> <head> <style> html, body { height: 100%;} </style> </head> <body> <p>A paragraph with a <button>button</button>.</p> <script> var body = document.querySelector('body'); var para = document.querySelector('p'); var button = document.querySelector('button');
// 버블링 body.addEventListener('mousedown', function () { console.log('Handler for body.'); });
// 버블링 para.addEventListener('mousedown', function () { console.log('Handler for paragraph.'); });
// 버블링 button.addEventListener('mousedown', function (event) { console.log('Handler for button.');
// 3번은 오른쪽을 뜻한다. mouse 오른쪽 버튼 클릭 시, 이벤트 전파를 중단한다. if (event.which === 3) event.stopPropagation(); }); </script> </body> </html>