[JavaScript] Block Scope, 함수 실행 컨텍스트, Scope Chain

방예서·2022년 4월 25일
0

JavaScript

목록 보기
2/6

220425

02 블록 스코프

실습 예제

let a = 100;
var b = 30;
const c = 40;
if (a>20) {
  let a = 10;
  var b = 10;
  const c = 20;
  console.log(a); //10
  console.log(b); //10
  console.log(c); //20
  
  if(c < 30) {
    const c = 5;
    console.log(c); //5
  }
}

console.log(a); //100
console.log(b); //10
console.log(c); //40

함수 할당형 방식(표현식)

const a = 10;
f(a);
const f = function(num) {
  console.log(num);
}

error
Uncaught ReferenceError: Cannot access 'f' before initialization

f가 초기화 되지 않아서 error가 난다.

var a = 10;
f(a);
var f = function(num) {
  console.log(num);

  const a = 10;
f(a);
const f = function(num) {
  console.log(num);
}

error
Uncaught TypeError: f is not a function

var는 undefined로 초기화 돼있다.
(a)를 실행하려고 하지만 아직 함수이지 않은 상태이기 때문에 에러가 난다.

함수 선언형 방식

const a = 10;
function f(num) {
  console.log(num);
}
f(a);

이렇게 함수를 선언(선언형 방식)하면 코드 평가단계에서 함수가 이미 선언이 된다.

const a = 10;
f(a);
function f(num) {
  console.log(num);
}

그러면 이런 식의 코드도 error 없이 잘 된다.
함수 hoisting이 일어난 것이다.

이 외에도 익명 함수로 선언하는 방법도 존재한다.


03 함수 실행 컨텍스트

const a = 10;
foo(a);
function foo(b) {
  let c = 20;
  console.log(a+b+c);
}

foo(a)에서 a가 b로 전달 될 때는 복사해서 가는 것이다.

함수의 this -> window 가르킨다.
(window.)foo(a) .window는 생략 되는 것이다.

  1. Global 실행 컨텍스트 생성 단계
    콜스택이 생긴다.

  2. Global 코드 평가 단계
    식별자 foo 라는 이름을 세팅하고 함수를 생성하여 할당한다.
    식별자 a도 할당한다. 아직 초기화는 되지 않은 상태이다.

  3. Global 코드 실행 단계
    변수 a가 undefined로 초기화 되었다가 10으로 할당된다.

  4. 함수 코드 평가 단계(함수 실행 컨텍스트 생성)
    함수 실행 컨텍스트가 생성되면서 콜스택이 쌓인다.
    인자 b는 undefined로 초기화 되고, 식별자 c도 할당한다. b와 c는 같은 scope에 존재한다.
    외부 환경(global)도 참조한다.

  5. 함수 코드 실행 단계
    b에 10 할당, c는 undefined로 초기화 되었다가 20으로 할당된다.

  6. log 함수 평가, 실행 단계
    log 함수용 실행 컨텍스트도 생긴다. 콜스택 쌓인다.
    실행하면서 변수가 함수 내부에 없으면 outer 환경(자신보다 콜스택 아래에 있는)으로 나가서 찾는다.

  7. 함수 코드 종료 단계
    코드 종료 되면 garbage collector로 할당 되었던 메모리 자동으로 해제한다. 콜스택이 0인 부분을 제외하고 사라진다.

콜스택이 생기면서 콜스택이 다르기 때문에 함수 내부에 있는 변수는 외부에서 접근할 수가 없다. (지역변수)

04 스코프 체인

Identifiers(식별자)를 찾는 일련의 과정.
자신이 속해 있는 scope의 지역 변수들을 참조하는데, 해당 콜스택에 참조값이 없다면 상위 레벨의 scope로 값을 찾아나가는 현상이다.

const a = 10;
foo(a);
{
  let b = 20;
  function foo(b) {
    let c = 20;
    console.log(a+b+c);
  }
}

함수를 block으로 감싼 경우이다.

error
Uncaught TypeError: bar is not a function

함수 호이스팅이 안되고 있다.
선언형으로 만들면 호이스팅 됐는데 왜일까?

코드 내부에 debugger 명시해주면 그때마다 코드가 멈추면서 확인할 수 있다.

debugger로 확인해보니
const a = 10;
여기서 함수 bar는 undefined로 되어있다.

다음 debugger 확인해보자

Global이 아닌 Block에서 함수 호이스팅이 일어나고 있다.

Global에도 bar를 사용할 수 있게 만들어졌다.
그래서 이 이후에 블록 밖에 함수를 실행하려고 해도 제대로 동작이 된다.

bar 안에 foo라는 함수가 있으면
안에서 바깥으로는 접근이 가능하지만
바깥에서 안으로는 접근할 수 없다.

실습 슬라이더 만들기

비동기 상황

html로 열었을 때

LiveServer로 열었을 때

왜 이럴까?

이미지를 불러오는 과정에서 비동기가 일어남.

 const liWidth = sliderLis[0].clientWidth;
  console.log(liWidth);

그래서 해당 부분에서 이미지를 불러오기 전에 이미지의 width를 가져오려고 하니까 자꾸 '0' 값이 뜨는 것이다.

그러면 어떻게 해야하지

window.onload = function() { }

이 함수 안에 모든 코드를 넣어줘서 이미지를 다 load 해온 후에 코드가 실행될 수 있게 한다.

function moveSlide(event) {
  event.preventDefault();
  console.log(event.target.textContent);
  console.log('눌렀다');
}

오늘까지의 코드

  • ul 넓이 계산해주기
  • console에 이전/다음 뭐 눌렀는지 나오게 하기
  window.onload = function() {
    const kindWrap = document.querySelector('.kind_wrap');
    const slider = kindWrap.querySelector('.slider');
    const sliderLis = slider.querySelectorAll('li');
    const moveBtn = kindWrap.querySelector('.arrow');

    const liWidth = sliderLis[0].clientWidth;
    
    //ul 넓이 계산해주기
    //이 값을 css로 전달해주어야함
    //.kind_wrap > .kind_slider 여기로
    const sliderWidth = liWidth * sliderLis.length;
    slider.style.width = `${sliderWidth}px`; //sliderWidth + 'px' 와 같습니다.

    //click listner 만들기
    moveBtn.addEventListener('click', moveSlide);

    function moveSlide(event) {
      event.preventDefault();
      console.log(event.target.textContent);
  };

event 객체

DOM과 관련된 이벤트가 발생하면 관련 정보는 모두 event객체에 저장된다.
이벤트 발생 요소, 이벤트 타입, 이벤트 관련 데이터도 저장된다.

모든 이벤트 객체는 이벤트의 타입을 나타내는 type 프로퍼티와 이벤트의 대상을 나타내는 target 프로퍼티를 가진다.

이벤트 전파 방식

이벤트 전파란 이벤트가 발생했을 때, 브라우저가 이벤트 리스너를 실행시킬 대상 요소를 결정하는 과정을 의미한다.

이벤트의 대상이 Window 객체와 같은 단일 객체라면 이벤트의 전파는 일어나지 않지만 Document 객체나 HTML 문서의 요소에서 이벤트가 일어나면 대상 요소를 결정하기 위해 이벤트의 전파가 일어난다.

  • 버블링(bubbling) 전파 방식
    이벤트가 발생한 요소부터 시작해서, DOM 트리를 따라 위쪽으로 올라가며 전파되는 방식입니다. (bottom-up)
    해당 요소의 리스너 실행 -> 부모 요소의 리스너 실행 -> ...
    가장 마지막의 window 객체까지 계속 이어진다.

  • 캡쳐링(capturing) 전파 방식
    이벤트가 발생한 요소까지 DOM 트리의 최상위부터 아래쪽으로 내려가면 전파되는 방식입니다. (top-down)
    window 객체의 리스너(최상위) -> document의 리스너(windown 자식) -> 자식 요소의 리스너 -> ...

preventDefault() 메소드

특정 이벤트는 미리 지정된 기본 동작을 가지고 있는데, 이런 기본 동작의 실행을 취소할 수 있는 메소드이다.

http://www.tcpschool.com/javascript/js_event_eventListenerCall

추가 공부

javascipt의 garbage collector

자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고 쓸모 없어졌을 때 자동으로 해제한다. (C에서는 malloc(), free()를 사용한다.)

하지만 이런 자동 메모리 관리 프로세스는 어떤 메모리가 필요한지 아닌지를 판단하는 것은 비결정적인 문제이기 때문에 궁극의 방법은 아니다.
이를 해결하기 위한 몇 가지 알고리즘이 있다.

참조-세기(Reference-counting) 가비지 콜렉션

"더 이상 필요없는 객체"를 "어떤 다른 객체도 참조하지 않는 객체"라고 정의한다. 이 객체 "garbage"라 부르며, 이를 참조하는 다른 객체가 하나도 없는 경우, 수집이 가능하다.

하지만 이 알고리즘에는 '순환 참조'를 다루는데 한계가 있다.
서로 참조하는 속성으로 생성된 두 객체는 함수 호출이 완료되고 할당된 메모리가 회수 되어야 하는데, 서로 참조하고 있으므로 이 알고리즘에서는 "garbage"의 대상으로 표시하지 않는다.

해당 알고리즘은 다음 알고리즘이 나오고 더 이상 쓰지 않는다.

표시하고-쓸기(Mark-and-sweep) 알고리즘

"더 이상 필요없는 객체"를 "닿을 수 없는 객체"로 정의한다.

이 알고리즘은 roots 라는 객체의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미한다). 주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 객체들, roots가 참조하는 객체가 참조하는 객체들... 을 닿을 수 있는 객체라고 표시한다. 그리고 닿을 수 없는 객체에 대해 가비지 콜렉션을 수행한다.

하지만 어떤 메모리 해제 시점을 수동으로 결정하는 것이 편리할 때가 있다. 이 방식을 이용하기 위해서 닿을 수 없는 객체를 명시하는 기능이 있어야 한다.

MDN-garbageCollector


querySelectorAll()

querySelector() 는 해당하는 객체 중 첫 번째 것을 찾아준다.
querySelectorAll() 는 해당하는 객체 모두를 찾아서 NodeList로 가져온다.

NodeList와 HTMLCollection의 차이?

  • 공통점

    • DOM API가 결과 값을 여러개 반환하기 위한 DOM Collection 객체
    • 유사 배열 객체이면서 iterable
    • 배열로 변환 후 사용이 권장
  • HTMLCollection
    getElementsByTagname, getElementsByClassName 메서드가 반환하는 객체
    노드 객체의 상태 변화를 실시간으로 반영한다. (Live객체)
    예를 들어 const로 HTMLCollection 객체의 길이를 가지고 있는 변수가 있다. const는 재할당이 불가능해도 HTMLCollection은 상태 변화를 계속 반영하기 때문에 길이가 변해도 실시간으로 반영되어 값이 바뀐다.

  • NodeList
    querySelectorAll 등의 메서드가 반환하는 NodeList 객체 (non-live 객체)
    노드 객체의 상태 변화를 반영하지 않는다. NodeList는 앞의 HTMLCollection과 다르게 노드가 변경되도 그 상태를 반영하지 않는다.
    또한 NodeList는 forEach 메서드를 상속 받아 사용할 수 있다.

https://yung-developer.tistory.com/m/79

forEach 메소드

주어진 함수를 배열 요소 각각에 대해 실행한다.

forEach()는 주어진 callback을 배열에 있는 각 요소에 대해 오름차순으로 한 번씩 실행한다. 삭제했거나 초기화하지 않은 인덱스 속성에 대해서는 실행하지 않는다.

const items = ['item1', 'item2', 'item3'];
const copy = [];

// for문
for (let i=0; i<items.length; i++) {
  copy.push(items[i]);
}

// forEach
items.forEach(function(item){
  copy.push(item);
});

MDN-forEach

profile
console.log('bang log');

0개의 댓글