forEach 메소드

배열의 요소를 하나씩 살펴보면서 반복 작업을 하는 메소드이다. forEach 메소드는 첫 번째 아규먼트로 콜백 함수를 전달받는데, 콜백 함수의 파라미터에는 각각 배열의 요소, index, 메소드를 호출한 배열이 전달된다. (index와 array는 생략가능, 파라미터 하나는 반드시 작성되어야 하는데 배열의 요소는 반드시 작성해야한다.)

const numbers = [1, 2, 3];

numbers.forEach((element, index, array) => {
  console.log(element); // 순서대로 콘솔에 1, 2, 3이 한 줄씩 출력된다.
});
//for...of 문과 비교해보기
const members = ['지철', '동욱', '요셉', '민지'];

//for...of문
for (let member of members) {
  console.log(`${member}님이 입장하셨습니다.`);
};

//forEach문
members.forEach(function (member) {
  console.log(`${member}님이 입장하셨습니다.`);
});

//forEach문 화살표함수로 반환하기
members.forEach((member) => {
  console.log(`${member}님이 입장하셨습니다.`);
});

//출력되는 값은 모두 같다
지철님이 입장하셨습니다.
동욱님이 입장하셨습니다.
요셉님이 입장하셨습니다.
민지님이 입장하셨습니다.

forEach문에 작성된 function (member)에서 member 파라미터는 for...of 문의 let member of members에 member 변수와 역할이 같다.

//인덱스 활용하기
const firstNames = ['범수', '얼', '효신', '수'];
const lastNames = ['김', '나', '박', '이'];
//다른 배열과 함께 활용할 때 같은 인덱스의 요소를 매칭할 수 있다.

firstNames.forEach((firstName, i, arr) => {
  console.log(`${lastNames[i]${firstName}님이 입장하셨습니다.}`);
  //배열 활용하기
  console.log(arr);
});

//출력되는값
김범수님이 입장하셨습니다.
['범수', '얼', '효신', '수']
나얼님이 입장하셨습니다.
['범수', '얼', '효신', '수']
박효신님이 입장하셨습니다.
['범수', '얼', '효신', '수']
이수님이 입장하셨습니다.
['범수', '얼', '효신', '수'] //forEach 메소드를 호출한 배열이 전달된 모습

+ 변수에 담지않고 배열값 자체에 forEach 메소드를 활용하는 경우도 있다. 상황에 따라서 콜백 함수 내에서 해당 배열이 필요할 때 활용 가능하다. (firstNames.forEach => ['범수', '얼', '효신', '수'].forEach)

Map 메소드

forEach와 비슷하게 배열의 요소를 하나씩 살펴보면서 반복 작업을 하는 메소드이다. 단, 첫 번째 아규먼트로 전달하는 콜백 함수가 매번 리턴하는 값들을 모아서 새로운 배열을 만들어 리턴하는 특징이 있다.

const numbers = [1, 2, 3];
const twiceNumbers = numbers.map((element, index, array) => {
  return element * 2;
});

console.log(twiceNumbers); // (3) [2, 4, 6]
//forEach문 대신 map 사용해보기
const firstNames = ['범수', '얼', '효신', '수'];
const lastNames = ['김', '나', '박', '이'];

firstNames.map((firstName, i, arr) => {
  console.log(`${lastNames[i]${firstName}님이 입장하셨습니다.}`);
  console.log(arr);
});

//출력되는값
김범수님이 입장하셨습니다.
['범수', '얼', '효신', '수']
나얼님이 입장하셨습니다.
['범수', '얼', '효신', '수']
박효신님이 입장하셨습니다.
['범수', '얼', '효신', '수']
이수님이 입장하셨습니다.
['범수', '얼', '효신', '수']

map 메소드는 콜백함수 내에서 리턴문을 작성하게되면 각각의 리턴값으로 구성된 새로운 배열이 결과로 리턴된다.

const firstNames = ['범수', '얼', '효신', '수'];
const lastNames = ['김', '나', '박', '이'];

firstNames.map((firstName, i) => {
  return lastNames[i] + firstName;
});

//그래서 이 메소드에 호출한 결과를 변수에 담아서 콘솔에 출력해보면
const fullNames = firstNames.map((firstName, i) => {
  return lastNames[i] + firstName;
});

//arrow function으로 변환했을 때(더욱 간결한 작성)
//const fullNames = firstNames.map((firstName, i) => lastNames[i] + firstName);

console.log(fullNames);

//결과값
(4) ["김범수", "나얼", "박효신", "이수"]
//작성한 콜백 함수의 리턴값들로 구성된 배열이 출력된다.

forEach 메소드는 리턴값이 없기 때문에 변수에 담았을 땐 항상 undefined 값을 갖는다.

const fullNames = firstNames.map((firstName, i) => lastNames[i] + firstName);

console.log(fullNames);

//출력
undefined

단순히 배열의 반복작업이 필요할 땐 forEach를, 반복 작업을 통해서 새로운 배열이 필요한 경우엔 map 메소드를 활용한다.

+ 두 메소드의 최대 반복 횟수는 메소드를 처음 호출할 때 그 당시 요소의 개수이다. forEach나 map 메소드는 콜백 함수 내부에서 반복중인 배열을 편집할 수도 있다. 매번 반복할 때마다 배열의 새로운 요소를 추가하게 되면 무한루프에 빠질 거라고 예상할 수도 있지만 실제로 동작해보면 배열의 요소 갯수만큼만 동작하고 끝난 모습을 확인 할 수 있다.

const members = ['나래', '가영', '종윤', '세영'];

members.forEach((member) => {
  console.log(`${member}님이 입장하셨습니다.`);
  members.push('김태호');
});

console.log(members);

//출력되는 값
나래님이 입장하셨습니다.
가영님이 입장하셨습니다.
종윤님이 입장하셨습니다.
세영님이 입장하셨습니다.
(8) ["나래", "가영", "종윤", "세영", "김태호", "김태호", "김태호", "김태호"]

반복 중에 배열의 길이가 줄어든다면 반복 횟수도 함께 줄어든다.

const members = ['나래', '가영', '종윤', '세영'];

members.forEach((member) => {
  console.log(`${member}님이 입장하셨습니다.`);
  members.pop();
});

console.log(members);

//출력되는 값
나래님이 입장하셨습니다.
가영님이 입장하셨습니다.
(2) ["나래", "가영"]

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

배열 메소드 Ⅲ (some, every)  (0) 2024.12.19
배열 메소드 Ⅱ (filter, find)  (0) 2024.12.19
에러와 에러 객체  (1) 2024.12.18
구조 분해 Destructuring  (0) 2024.12.17
모던한 프로퍼티 표기법  (1) 2024.12.17

자바스크립트에서 에러가 발생하면 그 순간 프로그램 자체가 멈춰버리고 이후의 코드가 동작하지 않는다.

console.log('시작!');

const title = '해피 크리스마스!';
console.log(title);

console.log('끝!');

//출력 값
시작!
해피 크리스마스!
끝!

//하지만 만약 실수로 코드를 잘못 입력하게 되면..?

console.log('시작!');

const title = '해피 크리스마스!';
console.log(title);
console.log(christmas); //선언하지 않은 변수 or 함수를 값으로 입력

console.log('끝!');

//출력 값
시작!
해피 크리스마스!
Uncaught ReferenceError: christmas is not defined at index.js:19
//에러가 발생한 순간 프로그램이 멈춰버려서 이후에 나와야 하는 끝!은 출력되지 못한다.

자바스크립트에서는 에러가 발생하면 에러에 대한 정보를 name과 message라는 프로퍼티로 담고 있는 에러 객체가 만들어지는데, 대표적인 에러 객체는 SyntaxError, ReferenceError, TypeError 이다.

//존재하지 않는 함수나 변수를 호출할 때 발생되는 레퍼런스에러(ReferenceError)
//Uncaught ReferenceError: christmas is not defined at index.js:1

//잘못된 방식으로 자료형을 다루려고 할 때 발생되는 타입에러(TypeError)
const title = '코끼리';
console.log(title());

//Uncaught TypeError: title is not a function at index.js:6

//문법에 맞지 않을 때 발생되는 신택스에러(SyntaxError), 코드를 실행시키기도 전에 에러를 발생시킨다.(코드실행X)
const ti-tle = '코끼리';
console.log(title);

//Uncaught SyntaxError: Missing initializer in const declaration index.js:11

에러 객체는 직접 만들 수도 있다. new 키워드와 에러 객체 이름을 딴 함수를 통해 에러 객체를 만들 수 있고, throw 키워드로 에러를 발생시킬 수 있다.

throw new TypeError('타입 에러가 발생했습니다.');

try...catch문

try {
  // 실행할 코드
} catch (error) {
  // 에러 발생 시 동작할 코드
}

try...catch문은 자바스크립트에서 대표적인 에러 처리 방법이다.
try문 안에서 실행할 코드를 작성하고, try문에서 에러가 발생한 경우에 실행할 코드를 catch 문 안에 작성하면 되는데, 이 때 try문에서 발생한 에러 객체가 catch문의 첫 번째 파라미터로 전달된다. 만약, try문에서 에러가 발생하지 않을 경우 catch문의 코드는 동작하지 않는다.

try...catch문은 실행이 가능한 코드 내에서 에러를 다룬다. SyntaxError처럼 코드를 실행조차 하지 않는 에러는 try...catch문을 활용할 수 없다. 실행이 가능한 코드에서 발생한 에러를 예외(Exception)이라고 하는데, 실행가능한 코드에서 발생한 에러를 다루는 과정을 가리켜서 예외처리(Exception Handling)라고 한다.

try {
  console.log('에러 전');
  
  const codeName = 'Kelly';
  console.log('codeName');
  
  coneName = 'Alex'; //에러가 발생하는 지점
  
  const language = 'JavaScript';
  console.log(language);
} catch (error) {
  console.log('에러 후');
};

//출력 되는 값
에러 전
Kelly
에러 후

try 블록 내에서 에러가 발생된 시점 이후로는 실행되지 않고, 에러가 발생하면 catch문이 실행됐다. 만약 에러가 발생하는 지점을 없애준다면 코드는

try {
  console.log('에러 전');
  
  const codeName = 'Kelly';
  console.log('codeName');
  
  const language = 'JavaScript';
  console.log(language);
} catch (error) {
  console.log('에러 후');
};

//출력 되는 값
에러 전
Kelly
JavaScript

"에러 후"가 출력되지 않는다. try 문 안에 에러가 없기 때문에 catch문 안의 코드가 동작하지 않은것이다.

function printMembers(members) {
  for (const member of members) {
    console.log(member);
  }
};

const teamA = ['영진', '정윤', '혜미', '수진'];
printMembers(teamA);

const codeName = { name: 'Kelly' };
printMembers(codeName);

const teamB = ['종훈', '철수', '태희', '재석'];
printMembers(teamB);

//출력되는 값
영진
정윤
혜미
수진
//Uncaught TypeError: title is not a function at index.js:11

위 코드를 예시로 봤을 때 printMembers(codeName); 에러로 인해서 그 아래 코드는 실행되지 않고 프로그램이 멈췄다. 이런상황에서 콘솔창에 teamB에 접근해봐도 변수 teamB는 선언조차 되지 않았다.

콘솔창에서 보는 에러 상황

이럴 때 함수 내부에 try...catch문을 활용하게 되면 try 문 안에서 에러가 발생하더라도 catch문에서 에러 객체를 다루기 때문에 프로그램을 멈추지 않고 그 이후에 코드들을 안전하게 실행시킬 수가 있다

function printMembers(members) {
  try {
    for (const member of members) {
      console.log(member);
    }
  } catch (err) {
    console.error(err);
    alert(`${err.name}가 발생했습니다. 콘솔창을 확인해 주세요.`);
  }
};

const teamA = ['영진', '정윤', '혜미', '수진'];
printMembers(teamA);

const codeName = { name: 'Kelly' }; //배열이 아닌 객체값 사용, 객체는 for...of 문을 사용할 수 없다.
printMembers(codeName);

const teamB = ['종훈', '철수', '태희', '재석'];
printMembers(teamB);

방금 전과는 다르게 에러가 발생했다고 alert 창이 알려준다. 

콘솔창도 확인해보면 teamA의 요소들은 정상적으로 출력이 됐고, 에러 메세지도 똑같이 출력이 되긴 했지만, 에러가 발생한 다음에도 catch문을 통해 에러를 다뤘기 때문에 프로그램이 멈추지 않는다. (훨씬 안정적으로 프로그램을 만들 수 있다.)

그리고 try...catch문에서 에러의 유무와 상관없이 항상 동작해야할 코드가 필요하다면 finally문을 활용할 수 있다.

try {
  // 실행할 코드
} catch (error) {
  // 에러가 발상했을 때 실행할 코드
} finally {
  // 항상 실행할 코드
}

다시 말하자면, try문에서 어떤 코드를 실행할 때 에러 여부와 상관 없이 항상 실행할 코드를 작성하는 것이다.

function printMembers(...members) {
  for (const member of members) {
    console.log(member);
  }
}

try {
  printMembers('영훈', '윤수', '동욱');
} catch (err) {
  alert('에러가 발생했습니다!');
  console.error(err);
} finally {
  const end = new Date();
  const msg = `코드 실행을 완료한 시각은 ${end.toLocaleString()}입니다.`;
  console.log(msg);
}

+ finally문에서 에러가 발생한 경우에는 다시 그 위에 있는 catch문으로 넘어가진 않는다. 마아아안약에 finally문에서도 에러 처리가 필요한 경우에는 try...catch문을 중첩해서 활용하는 방법이 있다.

try {
  try {
    // 실행할 코드
  } catch (err) {
    // 에러가 발생했을 때 실행할 코드
  } finally {
    // 항상 실행할 코드
  }
} catch (err) {
  // finally문에서 에러가 발생했을 때 실행할 코드
}

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

배열 메소드 Ⅱ (filter, find)  (0) 2024.12.19
배열 메소드 Ⅰ (forEach, map)  (0) 2024.12.19
구조 분해 Destructuring  (0) 2024.12.17
모던한 프로퍼티 표기법  (1) 2024.12.17
Spread 구문  (0) 2024.12.16

배열과 객체와 같이 내부에 여러 값을 담고 있는 데이터 타입을 다룰 때 Destructuring 문법을 활용하면, 배열의 요소나 객체의 프로퍼티 값들을 개별적인 변수에 따로 따로 할당해서 다룰 수가 있다.

// 배열 구조분해 Array Destructuring
const members = ['코딩하는지용', '글쓰는유나', '편집하는민환'];

const macbook = members[0]; //지용
const macbook = members[1]; //유나
const macbook = members[2]; //민환
//위 세 줄 코드를 구조분해 문법으로 작성하면 아래의 코드 한줄과 같다.
const [macbook, ipad, coupon] = members;
//할당 연산자 members의 왼편에 변수의 이름들이 배열로 선언되어 있고, 
//members 배열 자체를 할당해 버린 모습을 하고 있다. 즉, 배열을 분해해서 하나씩 할당하는 것.

//할당하는 값이 없거나 배열의 형식이 아닌 경우엔 에러가 발생한다.
//const [macbook, ipad, coupon]; 혹은 const [macbook, ipad, coupon] = 123;

console.log(macbook); // 코딩하는지용
console.log(ipad); // 글쓰는유나
console.log(coupon); // 편집하는민환

함수에서 default parater, rest parameter를 다루듯이 Destructuring 문법을 활용할 때도 기본값과 rest 문법을 활용할 수 있다.

// Array Destructuring
const members = ['코딩하는지용', '글쓰는유나', undefined, '편집하는민환', '촬영하는준하'];
const [macbook, ipad, airpod = '녹음하는규식', ...coupon] = members;

console.log(macbook); // 코딩하는지용
console.log(ipad); // 글쓰는유나
console.log(airpod); // 녹음하는규식
console.log(coupon); // (2) ["편집하는민환", "촬영하는준하"]
//rest parameter와 마찬가지로 나머지 요소를 가져오기 때문에 항상 마지막 변수에만 활용할 수 있다.

선언된 변수의 개수와 배열의 길이가 같아야 할 필요는 없다. 할당하는 배열의 길이가 길더라도, 인덱스에 따라 순서대로 할당이 되기 때문에 길이가 넘치는 요소는 어느 변수에도 할당되지 않는다. 배열의 길이가 적을 경우에는 남은 변수에 undefined 값이 반환된다.

배열의 구조분해 문법은 변수에 할당된 값을 서로 교환할 때도 활용할 수가 있다.

let macbook = '주원';
let ipad = '희준';

console.log('MacBook 당첨자:', macbook); //Macbook 당첨자: 주원
console.log('iPad 당첨자:', ipad); //iPad 당첨자: 희준

//배열 구조분해를 사용하지 않을 때 두 값을 교환해야 하는 경우
let temp = macbook; //임시로 값을 담아둘 변수를 만들어 활용했다.
macbook = ipad;
ipad = temp;

console.log('MacBook 당첨자:', macbook); //Macbook 당첨자: 희준
console.log('iPad 당첨자:', ipad); //iPad 당첨자: 주원

//배열 구조분해를 활용하는 경우. (할당연산자는 오른쪽 값을 왼쪽의 피연산자에 할당하는 동작을 한다.)
[macbook, ipad] = [ipad, macbook];

console.log('MacBook 당첨자:', macbook); //Macbook 당첨자: 희준
console.log('iPad 당첨자:', ipad); //iPad 당첨자: 주원

객체도 배열과 크게 다르진 않지만 객체는 인덱스가 아니라 프로퍼티 네임으로 여러 값들을 구분한다. 배열 구조분해에서 대괄호를 감쌌던 것처럼 객체를 분해할 때는 중괄호로 변수를 감싸준다.

// 객체 구조분해 Object Destructuring
const macbookPro = {
  title: '맥북 프로 16형',
  price: 3690000,
};

const title = macbookPro.title;
const price = macbookPro.price;
//위 코드 두 줄을 아래 한줄로 객체 구조분해 할 수 있다.
const { title, price } = macbookPro;

console.log(title); // 맥북 프로 16형
console.log(price); // 3690000

그외에는 배열의 구조분해처럼 존재하는데 예를 들어 객체에 존재하지 않는 프로퍼티 네임으로 변수가 선언이 되어 있으면 undefined 값이 할당 되고, 할당연산자를 통해서 기본값을 지정해 줄수도 있다. 

+ 선언된 변수의 개수와 배열의 길이가 같아야 할 필요는 없다. 할당하는 배열의 길이가 길더라도, 인덱스에 따라 순서대로 할당이 되기 때문에 길이가 넘치는 요소는 어느 변수에도 할당되지 않는다. 배열의 길이가 적을 경우에는 남은 변수에 undefined 값이 반환된다.

// Object Destructuring
const macbookPro = {
  title: '맥북 프로 16형',
  price: 3690000,
  memory: '16 GB 2667 MHz DDR4',
  storage: '1TB SSD 저장 장치',
};

const { title, price, color = 'silver', ...rest } = macbookPro;

console.log(title); // 맥북 프로 16형
console.log(price); // 3690000
console.log(color); // silver
console.log(rest); // {memory: "16 GB 2667 MHz DDR4", storage: "1TB SSD 저장 장치"}

...rest 처럼 나머지 프로퍼티를 하나의 객체로 모을 수도 있는데, 만약 이것을 제외하면 변수 이름은 항상 프로퍼티 네임과 동일해야 하는 걸까?

// Object Destructuring
const macbookPro = {
  title: '맥북 프로 16형',
  price: 3690000,
  memory: '16 GB 2667 MHz DDR4',
  storage: '1TB SSD 저장 장치',
};

const { title: product, price, color = 'silver', ...rest } = macbookPro;
//바꿔주고싶은 프로퍼티 네임: 바꿀 프로퍼티 네임

console.log(title); //Error!!
console.log(product); // 맥북 프로 16형

바꿔주고싶은 프로퍼티 네임: 바꿀 프로퍼티 네임으로 작성해주면 새로운 이름의 변수를 선언 할 수 있다. 코드를 실행해봤을 때 console.log(title)은 에러가 발생하지만 console.log(product)로 실행해보면 title 프로퍼티가 잘 할당되어 있는 것을 확인 할 수 있다.

//객체 내부의 프로퍼티 네임이 변수 이름으로 사용할 수 없는 경우
const launching = {
  title: '서울 어린이 동물원',
  thema: '동물원',
  price: 40000,
  operatingHours: '9 to 5',
  'serial-num': 'ABCDEFGHIJKLMNOP'
};
//따옴표를 활용하면 중간에 하이픈이 있어서 변수로 사용할 수 없는 이름도 프로퍼티 네임으로 사용할 순 있지만
//구조분해를 할 때는 반드시 새로운 이름으로 변수를 선언해야 한다.

const {title, thema, price, operatingHours, 'serial-num':serialNum } = launching;

//그리고 위 방식말고도 아래 방식으로 변수의 새로운 이름을 선언할수도 있다.

const propertyName = ''serial-num';
const { [propertyName]: serialNum } = launching;

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

배열 메소드 Ⅰ (forEach, map)  (0) 2024.12.19
에러와 에러 객체  (1) 2024.12.18
모던한 프로퍼티 표기법  (1) 2024.12.17
Spread 구문  (0) 2024.12.16
조건부 연산자(삼항 연산자)  (0) 2024.12.16

+ Recent posts