멋쟁이사자처럼(명지대) 3주차 수업

Crmal·2022년 5월 26일
0

3주차

목표

💡 자바스크립트를 좀 더 알아보자
     비동기에 대해서는 어느정도 알고가자

개념


  • 비구조화할당(구조분해할당)
    • 내용

비구조화 할당 이란?

💡 ES6에서 등장한 문법으로써, 배열이나 객체 속성을 해체하여 개별 변수에 값을 담을 수 있는 JavaScript 표현식을 말합니다.

또는 구조 분해 할당이라고 명칭 합니다.

왜 쓰나요?

💡 배열, 객체 내 값을 추출하는 코드가 매우 간단해진다.
     필요한 객체와 나머지 요소 분리가 매우 간단하다.
     기본값 지정이 가능하다.

어떻게 쓰나요?

만약, object가 다음과 같이 생겼다면, 프로퍼티(key값)을 변수로 선언해 바로 해당 object의 값을 이용할 수 있습니다.

const object = { a: 1, b: 2 };

const { a, b } = object;

/*
얘와 같아요
const a = object.a; // 하위 속성이 있을 때 쓸 수 있습니다
const b = object.b;
*/
console.log(a); // 1
console.log(b); // 2

함수에서도 가능합니다.

const object = { a: 1, b: 2 };

function print({ a, b }) {
  console.log(a);
  console.log(b);
}

print(object);

// 이렇게 쓰면 좋은점

function Example(a, b, c) {
  ...
}
Example(a=1, b=2, c=3) // 이렇게 순서를 맞춰 넣어줘야하는데

function Example({a, b, c}) {
  ...
}
Example({a:1, c:3, b:2}) // key 이름만 맞춰주면 된다~
  1. object를 print라는 함수의 인자로 넣어준다.
  2. print에선 들어오는 인자의 구조를 분해하여 받는다

변수명 변경

변수의 이름도 바꿀 수 있어요

const animal = {
  name: '멍멍이',
  type: '개'
};

const { name: nickname } = animal // animal의 name이라는 속성을 nickname으로 쓸꺼야

console.log(nickname); // '멍멍이'

배열도 됩니다

const array = [1, 2];
const [one, two] = array;

console.log(one);
console.log(two);

기본값 지정

만약 내가 지정한 변수보다 넣어줄 매칭 할 값이 없다면 undefined가 발생한다.

만약 이때 undefined가 아니라 기본적으로 넣어주는 값이 있다고 하면 기본 값 을지 정하는 것도 가능하다.

const fruit = ['Apple', 'Banana', 'Peach'];

const [Apple,Banana, Peach, Pear='Pear'] = fruit;

console.log(Apple);
console.log(Banana);
console.log(Peach);
console.log(Pear);

번외

const extracted = {
  name: name,
  languages: languages,
  value: value
}

이 코드와

const extracted = {
  name,
  languages,
  value
}

이 코드는 같은 의미입니다

객체는 { 속성명 : 값 } 으로 이루어져 있는 것을 다들 아실텐데,

속성명과 값의 이름이 같다면 : 를 생략할 수 있습니다!

  • 배열 내장 함수
  • 내용
  • forEach

for문이랑 비슷합니다

const superheroes = ['아이언맨', '캡틴 아메리카', '토르', '닥터 스트레인지'];

for (let i = 0; i < superheroes.length; i++) {
  console.log(superheroes[i]);
}
	
superheroes.forEach((hero, index, arr) => {
  console.log(hero, index, arr);
});

스크린샷 2022-04-30 오전 12.33.54.png

  • map

배열 안의 각 원소를 변환 할 때 사용 되며, 이 과정에서 새로운 배열이 만들어집니다.

const array = [1, 2, 3, 4, 5, 6, 7, 8];

const squared = [];
for (let i = 0; i < array.length; i++) {
  squared.push(array[i] * array[i]);
}

const squaredMap = array.map((item) => item * item);

console.log(squared. squaredMap);

  • indexOf

원하는 항목이 몇번째 원소인지 찾아주는 함수입니다.

const superheroes = ['아이언맨', '캡틴 아메리카', '토르', '닥터 스트레인지'];
const index = superheroes.indexOf('토르');

console.log(index); // 2
  • findIndex

배열의 원소중, 조건을 만족하는 원소의 index를 리턴 해줍니다

const todos = [
  {
    id: 1,
    text: '자바스크립트 입문',
    done: true
  },
  {
    id: 2,
    text: '함수 배우기',
    done: true
  },
  {
    id: 3,
    text: '객체와 배열 배우기',
    done: true
  },
  {
    id: 4,
    text: '배열 내장함수 배우기',
    done: false
  }
];

const index = todos.findIndex(todo => todo.id === 3);
console.log(index);

// 결과가 여러개인 경우 가장 빠른 원소의 index를 리턴
  • filter

배열에서 특정 조건을 만족하는 값들만 따로 추출하여 새로운 배열을 만듭니다.

const todos = [
  {
    id: 1,
    text: '자바스크립트 입문',
    done: true
  },
  {
    id: 2,
    text: '함수 배우기',
    done: true
  },
  {
    id: 3,
    text: '객체와 배열 배우기',
    done: true
  },
  {
    id: 4,
    text: '배열 내장함수 배우기',
    done: false
  }
];

const tasksNotDone = todos.filter(todo => todo.done === false);
console.log(tasksNotDone);

  • splice

배열에서 특정 항목을 제거할 때 사용합니다

첫번째 파라미터어떤 인덱스부터 지울지를 의미하고 두번째 파라미터는 그 인덱스부터 몇개를 지울지를 의미합니다.

const numbers = [10, 20, 30, 40];
const index = numbers.indexOf(30);

numbers.splice(index, 1);

console.log(numbers); // [10, 20, 40]
  • join

배열 안의 값들을 문자열 형태로 합쳐줍니다.

const array = [1, 2, 3, 4, 5];

console.log(array.join()); // 1,2,3,4,5
console.log(array.join(' ')); // 1 2 3 4 5
console.log(array.join(', ')); // 1, 2, 3, 4, 5
  • reduce

연산의 결과를 누적하여 최종 값을 반환합니다.

const numbers = [1, 2, 3, 4, 5];
const sum = array.reduce((accumulator, current, index, arr) => accumulator + current, 0);

console.log(sum);
  • accumulator : 누적 값
  • current : 현재 값
  • index : 인덱스
  • arr : 원래 배열 (sum을 의미)
  • 0 : 기본 값

콘솔을 이용해 단계 별 값을 봐봅시다

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce((accumulator, current, index, arr) => {
  console.log({ accumulator, current, index, arr });
  return accumulator + current;
}, 0);

console.log(sum);

  • 동기식 처리 / 비동기식 처리
  • 내용

동기식 처리

💡 요청을 보낸 후 응답을 받아야 다음 태스크가 수행되는 것
     응답 받기 전 까지 이후 태스크들은 블로킹된다.

  • 결과는 어떻게 될까요?

비동기식 처리

💡 병렬적으로 처리한다.
     태스크가 종료되지 않은 상태여도 대기하지 않고 다음 태스크를 실행한다.

대부분의 DOM이벤트 핸들러와 Timer함수, api요청은 비동기로 처리한다.

  • 결과는 어떻게 될까요?

1 → 3 → (1초 후) 2

  • 결과는 어떻게 될까요?

결과값은 “1”, “3”, “2” 순으로 찍혔다.
이는 setTimeout()메소드가 비동기적 API이기 때문이다.

위의 코드를 컴퓨터의 입장에서 해석해보면 다음과 같다.

  • 첫번째 줄에서 console.log("1");를 만나고 콘솔에 1st를 찍는다.
  • 두번째 줄에서 setTimeout() 메소드를 만나서 해당 매소드가 비동기적 매소드이기 때문에 이를 처리하는 다른 프로그램에 맡긴다.
  • 그러고 나서 곧바로 console.log("3")를 콘솔에 찍는다.
  • setTimeOut() 메소드를 처리하는 프로그램은 비동기적 API를 제외한 모든 코드가 실행 된 이후 결과를 콘솔에 찍는다.

따라서...

동기는 디자인이 비동기보다 간단하고 직관적일수 있지만 결과가 주어질 때 까지 아무것도 못하고 대기해야하는 문제가 있다. 

비동기는 동기보다 복잡하지만 결과가 주어지는데 시간이 걸려도 그 시간동안 다른 작업을 할 수 있어서 보다 효율적일 수 있다.

참고자료

https://velog.io/@daybreak/Javascript-비동기-처리

  • fetch
  • 내용

Fetch란?

💡 JavaScript에서 서버로 HTTP 요청을 보내고 응답을 받을 수 있도록 해주는 함수이다.

그렇기 때문에 기본적인 구조와 동작은 Promise 객체와 동일합니다. (Promise는 바로 다음에 배웁니다^^)

파라미터로 요청을 보낼 url을 입력해 주고 응답을 받아서 추가적인 작업 또한 해줄 수 있습니다.

then에서 응답 객체를 받고, catch에서 에러 요청이 발생했을 때, 에러 객체를 받습니다.

HTTP 요청을 기반으로 하기 때문에 HTTP 메서드를 이용할 수 있습니다.

GET요청

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then((response) =>
        console.log(response)
       );

응답 객체는 다음과 같습니다

Response {
  status: 200, 
    ok: true, 
      redirected: false, 
        type: "cors", 
          url: "https://jsonplaceholder.typicode.com/posts/1",}

→ 이건 응답 전체에 대한 정보

대부분의 REST API들은 JSON 형태의 데이터를 응답하기 때문에, 응답(response) 객체는 json() 메서드를 제공합니다.

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then((response) => response.**json()**)
        .then((data) => console.log(data));

이 메서드를 호출하면, 응답(response) 객체로 부터 JSON 포멧의 응답자바스크립트 객체로 변환하여 얻을 수 있습니다.

json() Response 스트림을 가져와 스트림이 완료될때까지 읽는다. 그리고 다 읽은 body의 텍스트를 Promise형태로 반환합니다.

받은 데이터는 다음과 같습니다.

{
  "userId": 1,
    "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit↵suscipit recusandae consequuntur …strum rerum est autem sunt rem eveniet architecto"
}

POST요청

fetch("https://jsonplaceholder.typicode.com/posts", {
  **method: "POST",
  headers: {
  "Content-Type": "application/json",
},
      body: JSON.stringify({
      title: "Test",
      body: "I am testing!",
      userId: 1,
      }),**
})
  .then((response) => response.json())
    .then((data) => console.log(data));

새로운 포스트 생성 위해서는 method를 POST로 지정해주고, 
headers에는 JSON 포맷 사용한다고 알려줘야 합니다. 
body에는 요청 전문을 JSON 포멧으로 넣어주세요.

결과는 다음과 같습니다.

PUT 요청

fetch("https://jsonplaceholder.typicode.com/posts/1", {
  method: **"PUT",**
  headers: {
  "Content-Type": "application/json",
},
      body: JSON.stringify({
      title: "Test",
      body: "I am testing!",
      userId: 1,
      }),
})
  .then((response) => response.json())
    .then((data) => console.log(data));

결과는 다음과 같습니다

→ 업데이트 된 결과

DELETE 요청

fetch("https://jsonplaceholder.typicode.com/posts/1", {
  method: "DELETE",
})
  .then((response) => response.json())
  .then((data) => console.log(data));

→ 데이터가 없는 이유는?

→ 삭제를 했으니 성공이라는 status 말곤 돌려줄 데이터가 없기 때문

  • Promise
    • 내용

💡 프로미스는 자바스크립트 비동기 처리에 사용되는 객체입니다.
     여기서 자바스크립트의 비동기 처리란
    ‘특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성’을 의미합니다

왜 필요한가?

  • 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.
  • 서버와의 요청을 동기적으로 처리하게 되면 해당 요청이 끝날 때 까지
    다음 스텝을 진행할 수 없기 때문에 사용자 경험에 좋지 않은 영향을 끼친다

>> 이를 해결하기 위해 Promise를 사용합니다. <<

api 호출을 하는 코드를 봐봅시다

// api를 호출하는 함수를 선언, 파라미터로 함수를 필요로함
function getData(callbackFunc) {
  // 밑의 줄이 실행되면 api 호출 이후, 파라미터로 받은 함수가 실행됨
  $.get('url 주소', function(response) {
    // 서버에서 받은 데이터 response를 파라미터로 받은 함수에 인자로 넘겨줌
    callbackFunc(response); 
  });
}

//해당 함수를 호출
getData(function innerFun(tableData) { //파라미터로 함수를 전달함
  console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});

getData() 를 정의하고 파라미터로 callbackFunc을 받는다.
api 요청이 성공하면 서버에서 받은 데이터인 response
파라미터인 callbackFunc의 파라미터로 전달해서 실행한다.

이런 느낌

위 코드에 Promise를 적용하면 다음과 같이 변한다.

// Promise 객체를 생성하여 반환하는 함수
function getData(callback) {
  return new Promise(function(resolve) {
    $.get('url 주소', function(response) {
      resolve(response); //성공을 반환
    });
  });
}

// getData()의 실행이 성공하면 then()이 호출됨
getData().then(function(tableData) {
  // resolve()의 결과 값이 여기로 전달됨
  console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});

getData()가 실행되면 Promise 객체를 반환하게 한다.

getData()를 호출하는 곳에서 성공적으로 반환되면 .then()을 통해 로직을 실행한다.

then() 에서 성공 후 로직을 살행한다

어렵죠?

저도 어렵습니다^^

본격적으로 파해치기 전에 프로미스의 3가지 상태에 대해 알아보자

Promise의 3가지 상태

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태 → 요청
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태 → 성공
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태 → 실패

Pending(대기)

  • 내용
  • new Promise()를 호출하면 대기(Pending) 상태가 된다.
  • new Promise()를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject이다.
  • 콜백함수란?
  • 선언은 내가할테니 실행은 너가 해줘라는 의미를 가진 함수
  • 전달하면 내부의 작업이 끝난 후 실행해준다
new Promise(function(resolve, reject) {
  // ...
});

Fulfilled(이행)

  • 내용
  • 콜백 함수의 인자중 하나인 resolve를 실행하면 이행(Fulfilled) 상태가 된다.
new Promise(function(resolve, reject) {
  resolve();
});
  • 그리고 이행 상태가 되면 아래와 같이 then()을 이용하여 처리 결과 값을 받을 수 있다.
function getData() {
  return new Promise(function(resolve, reject) {
    const data = 100;
    resolve(data); //data를 성공적으로 반환
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

→ 밑에 나오는 Promise는 getData() 함수의 반환값입니다.

→ getData() 함수는 return new Promise ~ 로 알 수 있듯이 Promise 객체를 반환하기 때문에 반환값이 존재합니다

Rejected(실패)

  • 내용
  • 콜백 함수 인자중 다른 하나인 reject를 호출하면 실패(Rejected) 상태가 됩니다.
new Promise(function(resolve, reject) {
  reject();
});
  • 실패 상태가 되면 실패한 이유(실패 처리의 결과 값)를 catch()로 받을 수 있다.
function getData() {
  return new Promise(function(resolve, reject) {
    reject("Request is failed"); //에러를 발생시키는 것
  });
}

// reject()의 결과 값을 err에 받음
getData().catch(function(err) {
  console.log(err); // Request is failed
});

fulfilled인 이유는 getData()가 실행된 것이 콘솔에 출력되어서 그렇다!

다음과 같이 여러 개의 Promise를 연결하여 사용할 수 있다.

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000); //2초 뒤에 실행
})
  .then(function(result) {
  console.log(result);  // result는?
  return result + 10; 
})
  .then(function(result) { 
  console.log(result); // result는?
  return result + 20; 
})
  .then(function(result) { 
  console.log(result); // result는?
});

catch 이후 then도 가능하다

new Promise((resolve, reject) => {

  throw new Error("에러 발생!");

}).catch(function(error) {

  alert("에러가 잘 처리되었습니다. 정상적으로 실행이 이어집니다.");

}).then(() => alert("다음 핸들러가 실행됩니다."));

프로미스 처리 흐름 - 출처 : MDN

5분 짜리 실습

콘솔에 찍어보고 위 내용을 이해해보세요~

  • Pending
function getPromise() {
  return new Promise(function (resolve, reject){
    setTimeout(() => resolve(123),5000);
  })
}

console.log(getPromise());

5초안에 콘솔을 열어야 pending으로 보임

  • Fulfilled
function getPromise() {
  return new Promise(function (resolve, reject){
    resolve(123)
  })
}

console.log(getPromise());

  • Rejected
function getPromise() {
  return new Promise(function (resolve, reject){
    reject(123)
  })
}

console.log(getPromise());

  • async/await
    • 내용

async & await란?

💡 async와 await는 자바스크립트의 비동기 처리 패턴 중 가장 최근에 나온 문법.
     기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와준다.

async & await 가 적용되지 않은 코드

fetchUser()가 api요청을 한다고 가정했을 때

function logName() {
  var user = fetchUser('domain.com/users/1', function(user) {
    if (user.id === 1) {
      console.log(user.name);
    }
  });
}

→ fetchUser라는 함수의 2번째 인자로 어떤 함수가 들어갔다.
     ㄴ> 이것은 요청 후, 바로 저 함수를 실행하라는 의미 (이것을 콜백함수라고 함)


바로 알아보기가 힘들죠?


async & await 가 적용된 코드

async function logName() { // 이건 비동기 함수야
  const user = **await** fetchUser('domain.com/users/1'); //응답을 기다릴꺼야
  /* user === { id: 1, name: John } 라 가정한다면*/
  if (user.id === 1) {
    console.log(user.name);
  }
}

async & await 간단한 예제

function fetchItems() { // 4
  return new Promise(function(resolve, reject) {
    const items = [1,2,3]; // 5
    resolve(items) // 6
  });
}

async function logItems() { // 2
  const resultItems = await fetchItems(); // 3
  console.log(resultItems); // [1,2,3] // 7
}

logItems(); // 1

방금 전 Promise를 배웠으니 알아보겠죠..? 아님말고

  • logItems()가 호출된다.
  • 내부에서 fetchItems()를 호출한다.
  • await가 있으므로 해당 함수의 결과값을 받을 때 까지 기다린다.
  • fetchItems함수는 Promise를 반환하는 함수로써 성공값으로 000을 반환한다.
  • 성공값인 000이 resultItems 변수에 담긴다.
  • 콘솔에 resultItems의 값이 출력된다.

async & await 예외 처리

promise에서 배운 것 처럼 catch를 이용하면 됩니다 (근데 살짝 다름)

async 함수에서 에러를 발생 시킬때에는 **throw** 를 사용하고, 에러를 잡아낼 때에는 try/catch 문을 사용합니다.

async function logTodoTitle() {

  try { //시도해라
    const user = await fetchUser();
  if (user.id === 1) {
    var todo = await fetchTodo();
    console.log(todo.title); // delectus aut autem
  }
} catch (error) { //에러 발생시 여기로
  console.log(error);
}

}

////////////////////////////////////////////

async function process() {
  try {
    throw new Error('에러 발생');
  } catch (e) {
    console.error(e+'123123');
  }
}
process();

대망의 실습

해당 url에 요청을 보내 응답값을 반환하는 함수를 만들고 (fetch 이용)

해당 함수를 호출해 반환한 응답값을 콘솔에 찍어주세요

p.s. json()도 비동기입니다!

function fetchUser() {
  const url = 'https://jsonplaceholder.typicode.com/users/1';

}

//
fetchUser().
  • 이벤트 시스템
  • 내용

이벤트란

💡 프로그래밍하고 있는 시스템에서 일어나는 사건(action) 혹은 발생(occurrence)인데,
     이는 여러분이 원한다면 그것들에 어떠한 방식으로 응답할 수 있도록 시스템이 말해주는 것

⇒ 한 마디로 말하자면 시스템에서 발생하는 것

이벤트 객체

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

ex) 마우스 이벤트 -> 마우스의 위치정보 포함 등키보드 이벤트 -> 누른 키의 코드 등

대표적인 속성

target

💡  실제 이벤트가 발생하는 요소

currentTarget

💡  이벤트 리스너가 달린 요소

이벤트 리스너

💡  이벤트에 대해 대기중인 것

⇒ 이벤트를 감지하고 있다~

  • 리스너의 종류

click, onchange, keyup, keydown 등등..

  • 리스너를 등록하는 법
// 윈도우 자체에 이벤트를 등록하거나
window.addEventListener('click', function() {});

// html element에도 등록할 수 있습니다
const button = document.querySelector('{클래스 이름}');
button.addEventListener('click', function() {});

이벤트 버블링

💡  자식부터 부모로 쭉 이벤트가 전파되는 것.

  • 전파되어 발생한 이벤트e.currentTarget으로 확인할 수 있다.
<body>
  <div
    class="one"
    style="width: 200px; height: 200px; background-color: antiquewhite"
    >
    <div
      class="two"
      style="width: 150px; height: 150px; background-color: azure"
      >
      <div
        class="three"
        style="width: 100px; height: 100px; background-color: cadetblue"
        ></div>
    </div>
  </div>

  <script>
    //div 태그를 전부 가져와 divs라는 이름에 배열로 할당한다.
    const divs = document.querySelectorAll("div");

    //divs의 길이만큼 반복하여 각 태그에 이벤트를 할당해준다.
    
    for (let i=0; i<divs.length; i++) {
        divs[i].addEventListener('click', logEvent);
      }

     // event 객체를 파라미터(인자) 로 받아 
     // **이벤트 객체**의 **이벤트가 달린 요소**의 **클래스 이름**을 출력해준다
    function logEvent(event) {
      console.log(event.currentTarget.className);
    }
  </script>
</body>

three를 클릭했을 때

각 태그마다 이벤트가 등록되어 있기 때문에 상위 요소로 이벤트가 전달되는 것을 확인할 수 있습니다. 만약 이벤트가 특정 div 태그에만 달려 있다면 위와 같은 동작 결과는 확인할 수 없습니다.

이와 같은 하위에서 상위 요소로의 이벤트 전파 방식을 이벤트 버블링(Event Bubbling)이라고 합니다.

위 코드의 logEvent함수 안에서 event.currentTarget.className이 아닌 event.target.className으로 바꾼다면 어떻게 출력될까요?

이벤트 캡쳐링

💡  부모부터 자식까지 이벤트가 전파되는 것.

<body>
  <div
    class="one"
    style="width: 200px; height: 200px; background-color: antiquewhite"
    >
    <div
      class="two"
      style="width: 150px; height: 150px; background-color: azure"
      >
      <div
        class="three"
        style="width: 100px; height: 100px; background-color: cadetblue"
        ></div>
    </div>
  </div>

  <script>
    const divs = document.querySelectorAll("div");

    for (let i=0; i<divs.length; i++) {
        divs[i].addEventListener('click', logEvent, {
          capture: true, // default 값은 false입니다.
        });
      }

    function logEvent(event) {
      console.log(event.currentTarget.className);
    }
  </script>
</body>

three를 클릭했을 때

stopPropagation()

💡 위 API는 **해당 이벤트가 전파되는 것을 막습니다.**

→ “난 이렇게 복잡한 이벤트 전달 방식 알고 싶지 않고, 그냥 원하는 화면 요소의 이벤트만 신경 쓰고 싶어요.”

이렇게 사용합니다

function logEvent(event) {
  event.stopPropagation();
}
// 이벤트 버블링 예제
for (let i=0; i<divs.length; i++) {
  divs[i].addEventListener('click', logEvent);
}

function logEvent(event) {
  event.stopPropagation(); //이벤트가 전파되는것을 막아라!

  // 클릭한 곳의 클래스명만 출력됩니다
  console.log(event.currentTarget.className); 
}
// 이벤트 캡쳐링 예제
for (let i=0; i<divs.length; i++) {
  divs[i].addEventListener('click', logEvent, {
    capture: true, // default 값은 false입니다.
  });
}

function logEvent(event) {
  event.stopPropagation();
  console.log(event.currentTarget.className); 
  // 어떤 결과가 예상되는지 채팅창에 적어주세요
}

/* 어딜 클릭하던 one이 찍힘 -> 맨 상위부터 시작이니까 */
  • spread, rest
  • 내용

Spread

spread 라는 단어가 가지고 있는 의미는 펼치다, 퍼뜨리다 입니다.
이 문법을 사용하면, 객체 혹은 배열을 펼칠수있습니다.

예를 들어서 다음과 같은 객체들이 있다고 가정해봅시다.

const slime = {
  name: '슬라임'
};

const cuteSlime = {
  name: '슬라임',
  attribute: 'cute'
};

const purpleCuteSlime = {
  name: '슬라임',
  attribute: 'cute',
  color: 'purple'
};

console.log(slime);
console.log(cuteSlime);
console.log(purpleCuteSlime);

cuteSlime엔 slime에 있는 속성이 존재하고

purpleCuteSlime엔 cureSlime에 있는 속성들이 존재합니다.

spread 문법을 이용하면 기존 형태에 원하는 속성만 추가 할 수 있습니다.

const slime = {
  name: '슬라임'
};

const cuteSlime = {
  ...slime,
  attribute: 'cute'
};

const purpleCuteSlime = {
  ...cuteSlime,
  color: 'purple'
};

const purpleAwesomeSlime = {
  ...cuteSlime,
  attribute : 'Awesome', //기존 속성이 중복되면 기존 속성을 대체합니다.
  color: 'purple'
};

console.log(slime);
console.log(cuteSlime);
console.log(purpleCuteSlime);
console.log(purpleAwesomeSlime);

→ 같은 결과가 나옵니다.

마찬가지로 배열에서도 사용할 수 있습니다

const animals = ['개', '고양이', '참새'];
const anotherAnimals = [...animals, '비둘기']; 
// animals값을 가져오고 '비둘기' 값을 추가 해줍니다.

console.log(animals);
console.log(anotherAnimals);

spread 연산자를 여러번 사용 할 수도 있습니다.

const numbers = [1, 2, 3, 4, 5];

const spreadNumbers = [...numbers, 1000, ...numbers];
console.log(spreadNumbers); // [1, 2, 3, 4, 5, 1000, 1, 2, 3, 4, 5]

Rest

💡  말 그대로 나머지 속성을 가져옵니다.

객체에서의 rest

const purpleCuteSlime = {
  name: '슬라임',
  attribute: 'cute',
  color: 'purple'
};

const { color, ...rest } = purpleCuteSlime;
// 처음에 봤던 구조 분해 할당으로써
// purpleCuteSlime.color 와 나머지 속성들을 할당합니다

console.log(color);
console.log(rest);

이름을 꼭 rest로 해야하나요?

→ 원하는 이름으로 해도 됩니다.

const purpleCuteSlime = {
  name: '슬라임',
  attribute: 'cute',
  color: 'purple'
};

const { color, ...cuteSlime } = purpleCuteSlime;

console.log(color);
console.log(cuteSlime);

배열에서의 rest

const numbers = [0, 1, 2, 3, 4, 5, 6];

const [one, ...rest] = numbers;

console.log(one); // 0
console.log(rest); // [1,2,3,4,5,6]

반대는 안되나요?

const numbers = [0, 1, 2, 3, 4, 5, 6];

const [..rest, last] = numbers;

→ 안됩니다~

함수에서도 사용이 가능합니다

// 어떤 파라미터들이 들어올지 모를때 사용합니다
// 들어온 값들을 모두 더한 결과를 반환해주는 함수
function sum(...rest) {
  //rest === [1, 2, 3, 4, 5, 6]
  return rest.reduce((acc, current) => acc + current, 0);
}

const result = sum(1, 2, 3, 4, 5, 6);
console.log(result); // 21

spread와 같이 사용할 수도 있습니다.

function sum(...rest) {
  return rest.reduce((acc, current) => acc + current, 0);
}

const numbers = [1, 2, 3, 4, 5, 6];
const result = sum(...numbers);
console.log(result);
  • numbers 배열안에 들어가는 값들이 바뀌어도 함수는 변경해주지 않아도 됩니다.

실습

함수에 n 개의 숫자들이 파라미터로 주어졌을 때, 그 중 가장 큰 값을 알아내세요.

function max() {
  return 0;
}

const result = max(1, 2, 3, 4, 10, 5, 6, 7);
console.log(result);
  • Scope
  • 내용

스코프란?

💡  우리가 변수 혹은 함수를 선언하게 될 때 해당 변수 또는 함수가 유효한 범위를 의미

스코프의 종류

  1. Global (전역) Scope: 코드의 모든 범위에서 사용이 가능합니다.
  2. Function (함수) Scope: 함수 안에서만 사용이 가능합니다.
  3. Block (블록) Scope: if, for, switch 등 특정 블록 내부에서만 사용이 가능합니다.

예시

const value = 'hello!';

function myFunction() {
  console.log('myFunction: ');
  console.log(value);
}

function otherFunction() {
  console.log('otherFunction: ');
  const value = 'bye!';
  console.log(value);
}

myFunction();
otherFunction();

console.log('global scope: ');
console.log(value); 

→ 이렇게 같은 이름을 가진 변수를 설정한다고 해서 기존에 Global Scope 로 선언된 value 변수의 값이 바뀌지 않습니다.

const value = 'hello!';

function myFunction() {
  const value = 'bye!'; //블록 스코프이자 함수 스코프를 가짐
  const anotherValue = 'world'; //블록 스코프이자 함수 스코프를 가짐

  function functionInside() {
    console.log('functionInside: ');
    console.log(value);
    console.log(anotherValue);
  }

  functionInside();
}

myFunction();

console.log('global scope: ');
console.log(value);
console.log(anotherValue);

0개의 댓글