본문 바로가기

JS

JS #9-자바스크립트 기초 개념9 (클로저)

클래스 설계 구조 : MVC

model

view

control

ㄴ 이 부분들을 모두 분리시켜서 클래스를 작성하는 것이 확장성을 늘릴 수 있다. 

 

 

웹 컴포넌트 는 무엇이냐 ? 

컴포넌트 -> 생성자함수

ㄴ 재사용, 복제가 용이하게 하려고 

 


 

 

변수의 유효범위와 클로저

->함수 내부에서 함수 외부에 접근할 수 있다. 

함수마다 실행 컨텍스트가 생기게 된다. (외부 -> 실행컨텍스트 / 내부 -> 실행컨텍스트)

함수가 값을 찾는 순서

1. 내 실행 환경 내에서 찾는다.

2. 매개변수에서 찾는다. 

3. 바깥 환경에서 찾는다. (outer Environment Reference)

ㄴ 내부 함수 bar가 외부 함수 foo에서 찾는 것!

ㄴ 함수를 타고, 타고 못찾으면 전역(global) 로 가게 된다!

 

 

 

 

그런데, 함수가 생성된 이후 내부에 있는 변수가 변경된다면, 영향을 받을까? 

 

코드블록

스코프 블록이 있을 때 블록 안에 작업만 가능하다. 외부에서 접근이 불가하다. 

ㄴ for, if, 함수 ... 모두 코드블록

 

중첩함수

 

함수 안에 다른 함수가 정의되어있는 것이 중첩함수이다.

 ㄴ 내부 함수는 그 외부에 정의된 함수에서 호출하겠다~ 

 

 

 

함수와 가비지컬렉터의 작동

함수의 로직이 모두 종료가 되면 썼던 변수들은 모두 -> 가비지 컬렉터에 의해 사라진다. (메모리 최적화를 위해)

함수가 실행될 때마다 변수의 메모리는 다시 세팅이 되는 것이다. 

 

 

함수가 밖에 있어야 해당 함수가 의도한 대로 돌아가게 된다. 

 

BUT!!! 전역이 오염된 것이다. 

 

let count 를 함수 내부에 넣고, 안전하게 선언을 할수 없을까? => closure

 

 

clusure

JavaScript의 매우 강력한 특성으로 독립적인 변수를 참조하는 함수를 말합니다.
즉, 클로저에 정의된 함수는 그것이 작성된 환경을 '기억'합니다.
function sum (a, b) {
    return a + b
}

const value = sum(1,2); // value()도 sum 함수와 동일하게 작동



function first() {
    let x = 10;
    function second() {
        let y = 20;
        return x + y;
    }

    // return second(); // second의 실행값을 리턴한다.

    return second // 세컨드 함수 본문이 담긴다.

}

const result = first(); //30이 담긴다.
//클로저 return second 했기 때문에 result() 함수로 실행이 가능하다.
//함수 본문을 내뱉었기 때문에 함수 바깥에서 내부 함수를 실행시키는 것이다 ...오




 

 

함수는 태어난 환경을 기억한다. 때문에 

second는 first라는 환경에서 태어났다는 것을 기억한다.

outer environmentr referencer를 통해 자기 바깥쪽의 first함수의 변수를 가져다 쓸 수 있다는 것을 기억한다. 

 

result() 를 실행시키면 안쪽 second 함수를 실행시킬 수 있다. 

 

 

currying function  함수 안에 함수를 실행시킬 수 있는 방법
first()() => 30이 출력됨

 

 

 

Closure
안쪽 함수가 바깥쪽 함수의 변수를 참조하고 있는 그런 형태
function counter() {
    let count = 0;

    function inner() {
        console.log(++count);
    }
    return inner;
}

const c1 = counter(); // 값이 늘어나면서 출력된다.

함수는 끝나면 Garbage Collection을 수집하지만

counter()가 c1에 counter()가 참조 대상이 되어있기 떄문에, 안쪽 환경인 inner함수가 실행이 되었다고 생각하지 않는다. 

결론적으로 안쪽 inner 함수가 바깥 환경을 참조하고 있기 때문에 GC가 변수를 수집해가지 않는다. 

--> 환경이 살아있기 때문에 count증가가 일어난다. 

 

 

closure는 GC가 들어오지 못하게 막는다! => 그래서 closure (폐쇄)

 

 

closure가 여러개면? 
-> 그만큼의 별도 환경을 만든다!

 

동일한 함수 참조 c2를 만들어 주더라도, 새로운 환경을 만들어낸다. 

따라서 c1의 값이 증가되었더라도 c2를 실행시켜줄때의 값은 초기값부터 다시 시작이 된다. 

 

 

closure는 객체도 리턴이 가능하다.

  • closure 인 function 자체를 return 할 수 있다. 
  • UFO에 earth 함수를 참조해 객체인 apple 을 리턴할 수 있다. 

 

 

 

addEventListner

  const first = document.querySelector('.first');


  function handleClick(){
   
  }
 
  first.addEventListener('click',handleClick)

사용자와의 interaction을 'event'라고 부른다. 

 

 

 

<실습1. 클릭할때마다 배경색상 변경하기>

 

1. closure 사용

function handleClick(){
   
    let isClicked = false;

return () => {

    if(!isClicked){
        document.body.style.background = 'seashell';
        }
        else {
        document.body.style.background = 'dodgerblue';
        }
   
        isClicked = !isClicked;
    }
}
 
  first.addEventListener('click',handleClick());

ㄴ 함수 호출 시 handleClcik()을 실행해주어야한다. 

 

 

2. iffe 이용

  const handleClick = (()=>{

    let isClicked = false;
 
    return function(){
 
      if(!isClicked){
        document.body.style.background = 'orange'
      }else{
        document.body.style.background = 'white'
      }
   
      isClicked = !isClicked;
 
    }
  })()
 
 
 
  first.addEventListener('click',handleClick)

ㄴiffe로 함수가 바로 실행되기 때문에 함수 handleClick만 넘겨주면 실행이된다. 

 

 

 

함수 배열을 return 하기

read = getter 역할 함수 / write = setter 역할 함수

useState 배열에서  read와 write 두개의 함수를 return 하고 싶을 때 배열 형태로 return [read, write]를 해준다. 

useState를 호출하는 부분에서 , 어차피 배열로 넘어오기 떄문에 '배열 구조 할당' 을 사용하면 된다. 

따라서 

const [state, setState] = userState(111) 을 하면 get과 set 함수들이 들어온다. 

 

 

 

closure의 정의 : JS 함수는 일급함수로 인식되기 떄문에 값처럼 취급이 된다. 

그래서 함수의 리턴값으로 함수를 내보낼 수 있고 그 방식을 사용해서

함수 안의 함수가 바깥 환경에 대한 변수를 참조하고 이를 함수 본문으로 리턴하게 된다면, 

밖에서 안쪽 함수를 제어함으로써 안쪽 함수의 바깥 환경을 참조하여 코드를 작성할 수 있다. 

여기서 가장 핵심이 되는 것은 GC이다. 

안쪽 함수는 바깥쪽 변수를 참조하고 있기 때문에 GC의 수집 대상이 아니며, 이는 함수의 실행이 끝나도 여전히 변수가 살아있는 이유이다. 

 

 

 

 

 


옵셔널체이닝

사용주의 이유 : 오류로 잡히지 않는다. 

 

평가대상에게 null이나 undefined가 오면 동작하지 않는 것이 옵셔널 체이닝이다!

 

const portableFan = {
    maker: 'fromB',
    brand: 'FD221',
    type: 'neckband',
    photos: {
      static: 'https://bit.ly/3OS50UD',
      animate: 'https://bit.ly/3P8646q'
    },
    getFullName() {
      return `${this.brand}, ${this.maker}`;
    },
  };
 
  // 아래 코드는 문제가 있어 런타임 중 오류가 발생합니다.
  console.log(portableFan.photos.animate);
 
  // 오류를 발생시키지 않으려면 아래와 같이 작성해야 합니다.
  if ('photos' in portableFan) {
    if ('animate' in portableFan.photos) {
      console.log(portableFan.photos.animate);
    }
  }
 
 
  // 위 구문을 논리곱 연산자를 사용한 방식으로 변경해봅니다.

  portableFan && portableFan.photos && portableFan.photos.animate

  // 위 구문을 옵셔널 체이닝을 사용한 구문으로 변경해봅니다.
 
  portableFan.photos?.animate

 

 

 

메서드로 사용하기

  const fullName = portableFan.getFullName?.();
  console.log(fullName);

protableFan에 getFullName 메서드가 없으면 실행 시키지말고, 있으면 실행시켜! >> 라는 뜻

 

 

 


 

web API

sync(동기) & async(비동기)

 

 

 

<동기>

  console.log(1);
  console.log(2);
  console.log(3);

자바스크립트 엔진이 하는 일 - 일이 끝날 때 까지 다음 일을 시작하지 않는다. 

자바스크립트엔진 : 웹 브라우저 내에 내장되어 있다. 

자바스크립트  엔진 자체가 비동기적으로 처리할 수는 없다. 따라서, browser의 도움을 받는다. 

 

 

 

 

<비동기>

setTimeout() , clearTimeout

setTimeout()을 통해 비동기로 실행하도록 만든다. 


  setTimeout(() => {
    console.log(2);
  }, 3000) //3초 뒤에 콜백이 실행된다!

  console.log(1);
  console.log(3);

결과값

 

setTimeout()의 리턴값은 랜덤한 수가 나온다.  

3000을 줬기 때문에 3초 간격으로 실행됨

ㄴ 이 값은 타이머의 id 값으로 타이머를 제어할 수 있다. 

 

clearTimeout() 에 id 값인 timer를 넣으면 타이머가 해제된다. 

 

 

setInterval() : 주기적으로 계속 실행되도록 하는 코드 
  const timer = setInterval(()=> {
    console.log('안녕!')
  }, 100)

  clearInterval(timer);

100을 줬기 때문에 0.1초 간격으로 실행

 

  let count = 0;
  const timer = setInterval(()=> {
    console.log('안녕!');
    count++;

    if(count == 100) {
        clearInterval(timer);
    }
  }, 10)

조건을 주어서 cout 100개 가지만 조건을 걸 수

API는 뭔가요 ? 
web Application Programming Interface

웹개발을 하는데 있어서, 백엔드 개발자가 특정 서비스를 프론트엔드 개발자가 구현하기 쉽도록 제공해주는 가이드

가이드문서를 만들어 놓은 것 즉, 사용 설명서! 

 

 

 


Math

 

실습1. Math 메서드 연습

/* ----------------------- */
/* Number Type             */
/* ----------------------- */


// 1억 (million)
// 0의 갯수가 많아 금액을 쉽게 파악하기 어렵습니다.
let riches = 100_000_000;

// 1,000 단위 구분하듯 사용할 수 있을까요?
riches = 100_000_000;

// 숫자 옆에 `e`를 붙여 0의 갯수를 설정할 수 있습니다.
riches = 1e8;


// 그렇다면 아래 작성된 숫자 값은 얼마일까요?
riches = 1.45e6; // → 1.45 * 10 ** 6


// 작은 수도 `e`를 사용해 표현할 수 있습니다.
riches = 1e-6; // → 1 / 10 ** 6


/* 어림수 ---------------------------------------------------------------- */

let number = 90_127.53100032;

// 내림
let floor = Math.floor(number);
console.log('floor: ', floor);

// 반올림
let round = Math.round(number);
console.log('round: ', round);

// 올림
let ceil = Math.ceil(number);
console.log('ceil :', ceil);

// 절삭(소수점 이하)
let truncate = Math.trunc(number);
console.log('truncate: ', truncate);

// 난수
let random = Math.random();
console.log('random: ', random);

// 여러 수 중, 최댓값
let max = Math.max(10, 5, 100, 90, 1000);
console.log('max: ', max);

// 여러 수 중, 최솟값
let min = Math.min(10, 5, 100, 90, 1000);
console.log('min: ', min);

// 거듭제곱
let pow = Math.pow(2, 53);
console.log('pow: ', pow);
 

 

 

실습2. 최소 최대값 사이의 난수를 반환하는 함수 

// 최소, 최대 값 사이 난수 반환 함수
let getRandomMinMax = function(minNum, maxNUm) {

    if( minNum > maxNUm ) throw new Error ('최솟값은 최댓값보다 적어야 합니다. ')
    const num = Math.round(Math.random() * (maxNUm-minNum) + minNum );
    return num;

}

let result = getRandomMinMax(min, max)
console.log(result);

Math.random은 0~ 0.9999의 난수를 발생시키며

maxNum에서 minNum 을 빼면 그 차가 나오는데, 이 차를 난수에 곱한 뒤

최솟값을 더해주면 해당 함수를 구현할 수 있다. 

 

최솟값이 최댓값보다 적은 값이 들어오면 안되므로, 예외처리 validation을 해준다.

 

 

 

 

 jsDoc

// JSDoc
/**
 * 2개의 인수를 받으며 가장 작은 수를 최솟값으로 인식합니다. 최솟값과 최댓값의 랜덤한 수를 반환합니다.
 * @param {number} min // {파라미터 타입} 리턴하는 값
 * @param {number} max // {파라미터 타입} 리턴하는 값
 * @returns number
 */
let getRandomMinMax = function(minNum, maxNUm) {

    if(min > max) throw new Error ('최솟값은 최댓값보다 적어야 합니다. ')
    const num = Math.round(Math.random() * (maxNUm-minNum) + min);
    return num;

}

let result = getRandomMinMax(min, max)
console.log(result);

ㄴ 나중에 함수 사용할 때 설명으로 유용함