실행 컨텍스트

컨텍스트란? 한국말로 ‘문맥’, 코드의 실행환경.

브라우저가 스크립트를 로딩해서 실행하는 순간 전역 컨텍스트 생성됨. 이는 페이지가 종료될 때까지 유지됨.

자바스크립트는 함수 스코프를 따름. 함수를 호출할 때마다 함수 컨텍스트가 하나씩 더 생김.

  • 전역 컨텍스트 하나가 생성 된 후 , 함수 호출시마다 컨텍스트가 생성됨.
  • 컨텍스트 생성 시 컨텍스트 안에 변수객체(arguments, variable), scope chain, this가 생성됨.
  • 컨텍스트 생성 후 함수가 실행 됨. 사용되는 변수들은 변수 객체안에서 값을 찾고 없으면 스코프 체인을 타고 올라감.
  • 함수 실행이 마무리 되면 컨텍스트는 사라짐 (클로저는 사라지지 않음), 페이지가 종료되면 전역 컨텍스트가 사라짐

전역 컨텍스트

전역 컨텍스트가 생성된 후 변수객체, scope chain, this가 들어옴.

전역 컨텍스트는 arguments가(함수의 인자) 없음. variable은 해당 스코프의 변수들임.

scope chain(자신과 상위 스코프들의 변수객체)은 자기 자신인 저역 변수 객체임.

this는 따로 설정되어 있지 않으면 window임. this를 바꾸는 방법은 new를 호출하는 것. 기본적으로 일반함수의 this는 window이며, new나 bind 같은 상황에서 this가 바뀜.

함수 컨텍스트

함수를 호출하는 순간 새로운 컨텍스트인 함수 컨텍스트가 생김. 전역 컨텍스트는 그대로. this는 따로 설정해준 적 없으면 window가 됨.

호이스팅

호이스팅이란 ? 변수를 선언하고 초기화했을 때 선언 부분이 최상단으로 끌어올려지는 현상을 의미. 함수 선언식 (ex ) function sayWow(){})으로 선언하였을 때는 식 자체가 통째로 끌어올려짐.

함수 표현식으로 선언할 경우에는 호이스팅이 일어나지 않음 (ex) var sayYeah = function() {})

클로저

클로저란 ? 비공개 변수를 가질 수 있는 환경에 있는 함수.

비공개 변수는 클로저 함수 내부에 생성한 변수도 아니고, 매개변수도 아닌 변수를 의미.

ex) function parent(){

var name = ”mom”

return function () {

console.log(name)

}

}

var closure = parent()

closure() // mom

여기서 name변수나, name변수가 있는 스코프에 대해서 클로저라고 부를 수 있음.

이를 컨텍스트적으로 분석해보면

  1. 전역 컨텍스트 생성 후 parent()함수 호출 시 parent 함수의 컨텍스트가 새로 만들어 짐
  2. var closure = parent() 할때 function을 return 하고 그 function을 선언할 때 scope chain은 lexical scoping을 따라서 [’parent 변수객체’, ‘전역 변수객체’]를 포함함. 따라서 closure을 호출할 때 scope체인은 closure 변수객체, parent 변수객체, 전역 변수객체 순으로 따라 올라가게 되고, scope chain을 통해 parent의 name 변수에 접근 할 수 있음.

클로저를 잘못 사용했을 시 성능 문제와 메모리 문제가 발생할 수 있음.

Map ?

es6에 추가된 자료형으로 Object와 비슷하지만 크게 다른점으로는

1. 순서가 있음

2 iterable 함

3. key값에 string값이 아닌 다른 타입이 들어갈 수 있음

4. 크기를 쉽게 알 수 있음

 

이렇게 있습니다.

 

그럼 위와 같은 Map이 주는 장점들로 인해 항상 Object보단 Map을 사용하는것이 좋을까요?

 

그렇지는 않습니다.

 

Object는 데이터를 저장하기 위한 굉장히 간단한 구조입니다. 그렇기 때문에 key값이 string인 간단한 데이터를 저장하기 위해서는 생성이 굉장히 쉽습니다. 또한 JSON으로 데이터를 어딘가에 전송해야 할 경우에는 Object를 사용해야합니다. Map은 아직 JSON으로 변환 되어 전달되지 않기 때문입니다.

 

그러나 Map은 Hash구조로 Object보다 순환이 빠르기 때문에 데이터를 추가하거나 수정하는 것이 빈번 할 경우 Map을 쓰는것이 좋습니다.

Map의 사용

map은 생성자를 이용하여 생성합니다.

const wrongMap = new Map()

 

set(key, value) 메서드를 이용하여 데이터를 set하고, has 메서드로 입력한 key에 해당하는 데이터 보유 유무를 확인합ㄴ디ㅏ. 또한 get 메서드를 이용하여 입력한 key에 대한 value를 얻을 수도 있습니다.

contacts.set('Jessie', {phone: "213-555-1234", address: "123 N 1st Ave"})
contacts.has('Jessie') // true
contacts.get('Hilary') // undefined

 

delete메서드를 이용하여 입력한 key에 해당하는 데이터를 지울수도 있으며 

size메서드로 map의 크기도 알 수 있습니다.

contacts.delete('Raymond') // false

 

 

 

 

Set과 마찬가지로 Map은 Map이외에 WeakMap이라는 자료형도 있습니다.

이 자료형은 마찬가지로 iterable 하지 않으며, 참조카운팅을 추가 하지도 않습니다.

 

WeakMap은 WeakSet과 달리 사용성이 좋은걸로 알고있으나.. 저는 사용해본적이 없어 사용할 일이 생긴다면 사용해보고 정리하여 따로 포스팅 하겠습니다.

 

Set 이란?

es6 에서 추가된 자료형으로

1. 배열의 중복을 제거 하고 싶을 때

2. 자료전체를 순회할 필요성이 있을 경우

3. 값의 유무를 판단 할 때

 

유용하게 쓸 수 있습니다.

 

1 번의 경우 Set의 중복된 값을 허용하지 않는다는 속성을 이용한 것이고

2,3 번의 경우 Set은 순회를 더 빨리 돈다는 점에서 유용하게 쓰입니다.

 

특히 저는 코딩테스트에서 중복제거를 위해 자주 사용하곤 합니다.

 

Set의 사용

set은 아래와 같이 생성 할 수 있으며, 생성자 안에는 iterable 한 객체들이 들어갈 수 있습니다. ex) Array

var mySet = new Set();
mySet2 = new Set([1, 2, 3, 4]);

set은 array의 push 처럼 add를 이용하여 값을 추가 할 수 있고, 중복을 허용하지 않습니다.

mySet.add(1); // Set { 1 }
mySet.add(5); // Set { 1, 5 }
mySet.add(5); // Set { 1, 5 }

set은 has 메서드를 이용하여 해당 값이 set에 포함되어 있는지의 여부를 확인할 수 있습니다. 

mySet.has(1); // true
mySet.has(3); // false, 3은 set에 추가되지 않았음
mySet.has(5);              // true

set의 size 메서드는 set의 크기를 반환하며, delete메서드를 이용하여 해당 요소를 제거할 수 있습니다.

mySet.size; // 5

mySet.delete(5); // set에서 5를 제거함

array를 set의 생성자 안에 넣어 set으로 변경할 수도 있지만, set을 array로 변경할 수도 있습니다.

// set을 Array로 변환하기 위해 전개 연산자 사용함.
console.log([...mySet]); // myArray와 정확히 같은 배열을 보여줌

이는 set은 iterable하기 때문에 spread연산자를 사용할 수 있어 가능합니다.

 

 

이 외로 WeakSet이라는 자료형도 존재합니다. 이는 set과 비슷하나 iterable하지 않고, 참조카운트를 증가시키기 않는 자료형입니다. iterable하지 않기때문에 안에 속성들을 순회하거나 탐색할 수 없습니다. 

 

WeakSet은 활용도가 미미하기 때문에 따로 다루지는 않습니다.

WeakSet의 활용도가 미미하기 때문인지 혹자들은 WeakMap과 통일성을 위해 만들어졌다고도 합니다..

 

자바스크립트에서 event를 컨트롤 할 때 preventDefault라는 메서드를 자주 접하게 된다.

 

event.preventDefault()는 해당 event가  cancelable 할때 (cancelable이 true일 때) 해당 이벤트의 기본 동작의 수행을 막는 메서드이다. 모든 event가 cancelable 하지는 않으니 모든 이벤트에 유효한 메서드는 아니다. cancelable 하지 않은 이벤트는 preventDefault를 호출해도 아무 반응이 일어나지 않는다.

 

 

예를 들어서 체크박스의 클릭이벤트를 막아 체크박스를 체크하지 못하게 만든다거나, submit 이벤트에 사용하여 데이터를 전송하지 못하게 하는 등 다양한 방면에서 사용 가능하다.

 

그러나 preventDefault는 이벤트 전파를 막지는 못합니다. 상위요소로의 이벤트 전파를 막기 위해서는 stopPropagation() 또는 stopImmediatePropagation()을 함께 사용해야합니다.

 

참고

https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault

 

Event.preventDefault() - Web API | MDN

Event 인터페이스의 preventDefault() 메서드는 어떤 이벤트를 명시적으로 처리하지 않은 경우, 해당 이벤트에 대한 사용자 에이전트의 기본 동작을 실행하지 않도록 지정합니다.

developer.mozilla.org

 

1.map

각 요소에 대해 주어진 함수를 수행하는 결과를 모아 새로운 배열을 반환한다.

ex) let newArray = testArray.map((value, index, array) => {

           // value : 현재 요소의 값, index : 현재 요소의 인덱스, array : 원본 배열
            return value * 2;
       });

 

2.filter

각 요소에 대해 주어진 함수의 결과값이 true인 결과를 모아 새로운 배열을 반환한다.

ex) let newArray = testArray.filter((value, index, array) => {

           // value : 현재 요소의 값, index : 현재 요소의 인덱스, array : 원본 배열
            return value * 2 > 0 //boolean 값 이어야 한다.
       });

 

map과는 다르게 return 값이 boolean 이고, true인 결과를 모아 새로운 배열을 반환하는데, 메서드 이름 그대로 , 어떤 배열을 조건에 맞게 필터링할 때 (ex validation을 체크해서 새로운 배열을 만들 때) 사용하기 쉽다. 

 

3.reduce

배열 각 요소에 대해 reducer함수를 실행하고 배열이 아닌 하나의 결과값을 반환한다.

ex) const numberList = [1, 2, 3, 4];
 const sum = numberList.reduce((acc, value) => { 
        //acc : 축적된 데이터 value : 현재 요소의 값
        return acc + cur; 
    }, 10 // 초기 값);
예를들어 위와 같이 사용하였을 때는 10 + 1 + 2 + 3+ 4 의 계산결과인 20이 sum에 할당 되게 된다.
초기값에 빈 배열을 할당하고 value를 그 배열에 push 하는 형태로 사용하면 새로운 배열을 만들어 낼 수 있다는 점에서 filter와 map을 다 아우를 수 있는 메서드이다. 다양한 방면에 적용이 가능하고 퍼포먼스 적인 측면에서도 가장 낫기 때문에 가장 많이 쓰인다. 

 

 
 

자바스크립트 런타임 구성요소

setTimeout 의 콜백함수가 delay시간 이후에 정확하게 호출되지 않는 이유는 자바스크립트 런타임 구성요소를 알아야 이해가 쉽다.

 

자바스크립트는 기본적으로 싱글 스레드 이므로 한개의 작업만을 다룰 수 있다. 그렇기 때문에 하나의 프로그램에서 동시에 하나의 코드만 실행할수있다. 

 

1) Call Stack

 자바스크립트 메서드 호출 시 실행 순서대로 call stack에 쌓이게되고(push), 함수 실행이 완료되면 스택에서 제거된다.(pop)(선입선출)

setTimeout도 호출되면 callStack에 먼저 쌓이게된다. 이후 호출은 완료되었기 때문에 stack에서 지워지게된다. 

 

2) Callback Queue

 

WebAPI의 콜백이 쌓이는 곳이다. 설정했던 delay시간이 지나고 나면 setTimeout의 콜백이 쌓이게 된다.

Web API는 자바스크립트가 실행되는 런타임 환경에 존재하는 별도의 API 이다. (V8 소스코드에는 존재하지 않는다.) ex) setTimeout, setInterval, setImmediate 등..

 

3) Event Loop

이벤트 루프는 콜 스택이 빌 때까지 기다린 후 콜백 큐에 있는 콜백을 콜 스택에 넣어주는 역할을 한다. 

 

다시 한번 설명하면 1. setTimeout이 실행된 시점에 Call Stack에 setTimeout이 쌓이게 되고, 호출이 완료된 후 제거된다.

2. 호출 이후 지정한 delay시간이 지나고나면 setTimeout의 콜백함수가 Callback Queue에 쌓이게 된다.

3. 이벤트 루프는 콜스택이 비게되면 콜백 큐에 있는 콜백함수를 Call Stack에 넣어준다.

 

이때 Call Stack에 다른 할일이 많이 쌓여있다면 콜백함수가 호출되는 시점은 더 늦어질 것이다. 그렇기 때문에 setTimeout으로 지정한 delay시간이 지나고 나서 지연시간이 생기게 된다. 

 

 

setTimeout 이란?

일정 시간이 지나고 지정한 코드를 실행시키는 메서드로 보통 아래와 같은 형태로 자주 사용한다.

setTimeout(() => {console.log("첫 번째 메시지")}, 5000);
setTimeout(() => {console.log("두 번째 메시지")}, 3000);
setTimeout(() => {console.log("세 번째 메시지")}, 1000);

위에서 , 뒤에 있는 숫자가 지연 시간을 나타내는데, 단위는 밀리세컨드이다. (1000 = 1초)

 

정확히 지정한 시간 이후 코드가 실행될까?

그런데 실제로 사용해보면 정확하게 지정한 시간이 지났을때 코드가 실행되지 않고 지연되는 경우가 발생한다. 글쓴이의 경우 스크롤을 이동시킬 때 setTimeout에 딜레이를 0으로 주고 그 안에서 scrollTo메서드를 사용할 때와 setTimeout으로 감싸지 않고 scrollTo를 사용할 때 결과에 차이가 발생하여(setTimeout으로 감싼 경우에만 스크롤이 이동하였다) setTimeout에대해 더 찾아보게 되었고, 그 결과 다음과 같은 문서를 보게 되었다. 실제로 찾아보면 scroll이동 이벤트는 많은 사람들이 setTimeout과 함께 사용하는 것을 추천하고 있다.

 

https://developer.mozilla.org/ko/docs/Web/API/setTimeout#%EB%94%9C%EB%A0%88%EC%9D%B4%EA%B0%80_%EC%A7%80%EC%A0%95%ED%95%9C_%EA%B0%92%EB%B3%B4%EB%8B%A4_%EB%8D%94_%EA%B8%B4_%EC%9D%B4%EC%9C%A0

 

setTimeout() - Web API | MDN

전역 setTimeout() 메서드는 만료된 후 함수나 지정한 코드 조각을 실행하는 타이머를 설정합니다.

developer.mozilla.org

 

위 문서에 나온 내용을 요약해보면

1. 중첩 타임아웃 사용시

브라우저는 setTimeout 호출이 5번 이상 호출된 경우 4ms의 최소 타임아웃을 강제함

2. 비활성탭의 타임아웃

백그라운드 탭으로 인한 부하(와 그로 인한 배터리 사용량)를 경감하기 위해, 브라우저는 비활성 탭에서의 최소 딜레이에 최소 값을 강제함

3. 페이지, 운영체제, 브라우저가 다른 작업으로 인해 바쁠 경우

페이지, 운영체제, 브라우저가 다른 작업으로 인해 바쁠 경우 타임아웃이 예상보다 늦게 실행될 수 있습니다.

 

나의 경우 3번에 해당하는 경우라고 추정하였다. (서버에서 데이터를 불러오고, 이를 이용하여 렌더링을 마치고 나서 스크롤이 생긴 이후 스크롤을 이동시켜야 하기 때문에)

 

이를 더 자세히 이해하기 위해서는 자바스크립트 런타임에 대한 이해가 필요한데, 이는 다음장에서 설명하도록 하겠다.

+ Recent posts