모듈(module)

모듈은 간단하게, 자바스크립트 파일 하나라고 할 수 있다.

내가 어떤 프로그램을 만든다고 했을 때 다양한 기능을 추가하다보면 어느순간 한 파일에 작성한 코드가 100줄, 1000줄이 넘어가는 경우가 생긴다. 하나의 파일에 많은 코드를 작성하게 되면 각 코드들의 의미를 빠르게 파악하기가 어렵고 기능수정이 필요할 때도 그부분을 찾기가 쉽지 않아 코드를 관리하기 어려워진다. 그래서 많은 기능이 필요한 프로그램은 하나의 파일로 관리하는게 아니라 각 기능별로 여러개의 파일로 분리해서 관리하는 것이 좋다.

공통된 기능이나 특별한 목적에 따라 각각의 파일로 분리하는 과정을 모듈화(Modularization)라고하고 각 파일 하나하나를 모듈이라고 부른다.

복잡하고 많은 양의 코드를 기능에 따라 각각의 파일로 나눠 관리하면
1. 코드를 좀 더 효율적으로 관리할 수 있고,
2. 비슷한 기능이 필요할 때 다른 프로그램에서 재사용 할 수도 있다는 장점이 있다.


모듈 파일이 가져야 할 조건

1. 모듈이 되는 파일은 이 파일만의 독립적인 스코프를 가져야 한다. 모듈 파일이 가지는 독립적인 스코프를 모듈 스코프 라고 한다.

모듈 스코프(Module Scope)

모듈 파일 안에서 선언한 변수나 함수는 외부에서 자유롭게 접근할 수 없고, 이 파일 안에서만 사용이 가능해야한다.

하나의 폴더 안에 index.html, index.js, print.js 파일들이 위치해 있다고 생각해보자. 서로 다른 파일에 자바스크립트 코드가 존재하지만 마치 하나의 파일에 존재하는 것처럼 스코프를 공유하게 된다.

//index.html
<body>
  <script src="print.js"></script>
  <script src="index.js"></script>
</body>
//print.js
const title = 'Hello Welcome to the Code World!';

function print(value) {
  console.log(value);
};

//index.js
print(title);

//print(title) 출력값
Hello Welcome to the Code World!

print.js에서 선언한 변수와 함수를 index.js에서 사용하고 있지만 코드를 실행해보면 에러가 발생하지 않고 실제로 두 파일에 나눠진 코드가 마치 하나의 파일에 존재하는 것처럼 잘 동작하는 모습을 확인 할 수 있다.

이 현상이 에러가 발생하지 않아 문제가 없는것처럼 느껴질 수 있지만 생각보다 큰 문제가 될 수 있다.

html에서 script 파일을 연결할 때 print.js를 먼저 연결하고 아랫줄에 index.js를 연결해줬다. 예를들어 아래쪽 파일인 index.js에서 코드를 작성할 때 위쪽 파일인 print.js에서 선언한 변수나 함수와 이름이 중복되면 이 값이 덮어써지면서 코드를 실행했을 때 의도치 않은 결과가 나타날 수 있고 SyntaxError가 발생할 수도 있다.

//print.js
const title = 'Hello Welcome to the Code World!';

function print(value) {
  console.log(value);
};

//index.js
print(title);

function print(valie) {
  console.log('이건 새로운 print 입니다.');
};

//print(title) 출력 값
이건 새로운 print 입니다.

------------------------------------------
//print.js
const title = 'Hello Welcome to the Code World!';

function print(value) {
  console.log(value);
};

//index.js
print(title);

function print(valie) {
  console.log('이건 새로운 print 입니다.');
};

const title = '너도 할 수 있어 코딩!';

//출력되는 값
//syntax Error

이런 문제를 방지하기 위해서 각각의 모듈 파일이 다른 파일과는 스코프를 함부로 공유할 수 없도록 모듈 스코프를 가지고 있어야 한다.

HTML파일에서 자바스크립트 파일을 불러올 때 모듈 스코프를 갖게 하려면 script태그에 type속성을 module이라는 값으로 지정해 주어야 한다.

//index.html
<body>
  <script type="module" src="index.js"></script>
  <script type="module" src="print.js"></script>
</body>

이렇게 하고 실행을 눌러보면 에러가 발생하는데, 브라우저에서 내 컴퓨터 안에 있는 HTML 파일을 불러오게 되면 자바스크립트의 모듈 문법은 사용할 수가 없다.

에러가 뜬 출력화면

자바스크립트의 모듈 문법을 만들 때 문법 자체의 보안 요구사항으로 일부러 에러가 발생되도록 설계했기 때문이다.


이 문제를 해결하려면 브라우저에서 직접 파일을 불러오는 방식이 아니라 서버를 통해서 이 HTML 파일을 실행해야 한다. 서버를 통해 HTML을 실행하는 방법은 굉장히 다양하지만 VS Code의 확장기능을 활용해서 간단하게 해결할 수도 있다.

vs code의 확장 프로그램 live server

설치를 한 후 go live 버튼을 눌러보면 서버를 통해 실행된 TML 파일이 자동으로 열리게 된다. 서버를 통해 실행된 HTML 파일에서 콘솔을 열어보면 index.js에서 title이 선언되지 않았다라는 오류가 발생하는걸 확인 할 수 있다.

왜냐하면 index.html에서 script를 연결할 때 type 특성에 module이라는 값을 지정해줬기 때문에 이 파일들은 각각 모듈 스코프를 갖게 되서 print.js 파일 내에 선언한 변수나 함수는 다른 외부 파일과 스코프를 공유하지 않게 된 것이다.

기본적으로 모듈 스코프를 가지고 특별한 문법을 통해서 서로 다른 파일들끼리 변수나 함수를 공유하는 것이 모듈 문법의 핵심이다.


모듈 문법

자바스크립트의 모듈 문법은 기본적으로 export(내보내다)와 import(불러오다) 다.

모듈 스코프를 가진 파일에서 외부로 내보내고자 하는 변수나 함수를 export 키워드를 통해 내보내고, 모듈 파일에서 내보낸 변수나 함수들은 다른 파일에서 import 키워드를 통해 가져온다.

// printer.js
export const title = 'CodePrinter';

export function print(value) {
  console.log(value);
};

//이제는 이 값들을 다른 파일에서도 사용할 수 있다. 하지만 무조건 제한 없이 사용할 수 있는건 X
//변수나 함수를 사용하고자 하는 파일에서 한번더 불러오는 과정을 거쳐야 사용할 수 있다.
// index.js
import { title, print } from './printer.js';

print(title);

//파일 내에서 사용할 함수나 변수를 쉼표로 구분해서 작성해준 다음 from 키워드와 모듈 파일의 경로를 작성해준다.

이름 바꿔 import 하기

import 키워드를 통해 모듈을 불러올 때 as 키워드를 활용하면 import하는 대상들의 이름을 변경할 수 있다.
import 할 변수나 함수 이름을 조금 더 간결한 이름으로 바꾸거나, 혹은 더 구체적으로 바꾸고 싶을 때 활용할 수 있다.
뿐만 아니라 이름을 바꿔서 import 하면 여러 파일에서 불러오는 대상들의 이름이 중복되는 문제를 해결할 수 있다.

// printer.js
export const title = 'CodePrinter';

export function print(value) {
  console.log(value);
};
//members.js
export const title = 'CodeMembers';

export const data = [
  '지민', '태연', '유나', '연준', '태형', '정국',
  '해린', '민정', '사랑', '승희', '소유', '효리'
];
//index.js
import { title as printerTitle, print, printArr } from './printer.js';
//title이라는 변수나 함수이름을 printerTitle로 바꾼다.
import { title, data as members } from './members.js';

printer(title); //CodeMembers
arrPrinter(members); //(12) ['지민', '태연', ..., '소유', '효리']

한꺼번에 import 하기

모듈 파일을 import 할 때 그 파일에서 export 하는 대상을 모두 가지고 올 필요는 없다. 실제로 파일에서 사용하는 것만 import를 하면 된다. 지금은 코드가 짧아 export 대상이 몇 개 없지만 만약 모듈 파일에서 export 하는 대상이 훨~씬 많고 다른 파일에서 import를 할 때도 그 대상들을 모두 불러와야 한다면 생각보다 작성해야 될 코드도 많아지고, 자연스럽게 가독성도 나빠진다.(여기서 이름까지 바꾼다면 코드는 더 복잡해진다.)

import할 때 와일드카드 문자(*, Wildcard Character)와 as를 활용하면 모듈 파일에서 export하는 모든 대상을 하나의 객체로 불러올 수 있다.

//print.js에서 export하는 모든 대상이 이 이름(printerJS)에 전달이 된다.
import * as printerJS from './printer.js';

console.log(printerJS.title); // CodeitPrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }

이렇게 활용하면 조금 더 간결하게 사용할 수 있고, 값을 가지고 올 때 프로퍼티 형식으로 값을 불러오기 때문에 여러 모듈을 불러 올 때 이름이 중복 될 일이 없다.

하지만 이 파일에서 필요하지 않은 대상들도 모두 불러오는 단점도 있어서 코드가 항상 효율적이라고 할 순 없다. (무분별하게 *를 사용하지 말고 필요에 따라 적절하게 활용하는것이 좋다.)


한꺼번에 export 하기

변수나 함수 앞에 매번 export 키워드를 붙일 수도 있지만, 선언된 변수나 함수를 하나의 객체로 모아 한꺼번에 내보낼 수도 있다.
이때 as 키워드를 활용하면 이름을 변경해서 export할 수도 있다.

const title = 'CodeitPrinter';

function print(value) {
  console.log(value);
}

function printArr(arr) {
  arr.forEach((el, i) => {
    console.log(`${i + 1}. ${el}`);
  });
}
// export 키워드 다음에 내보낼 대상을 중괄호로 묶어주면 된다.
export { title as printerTitle, print, printArr };

그러면 선언마다 export를 붙여주지 않아도 되고, 어떤것들이 export 되는지 한눈에 알아볼 수 있다는 장점이 있다.

import를 할 때처럼 as 키워드를 사용할수도 있는데, 모듈 파일에서 미리 이름을 바꿔서 export를 할 수도 있다.


default export

export 키워드를 사용할 때와는 다르게 default 키워드가 붙으면 그 뒤에는 반드시 하나의 대상만 내보낼 수가 있다. 변수나 함수 이름으로 export하는 것 뿐만 아니라 바로 값 하나를 전달할 수도 있다. 한가지 주의해야 할 점은 default 키워드는 모듈 파일내엣 딱 한번만 사용할 수 있다는 점이다.

const title = 'CodePrinter';

function print(value) {
  console.log(value);
}

export default print; //변수나 함수 뿐만 아니라
//export default 'CodePrinter';  이렇게 값을 바로 입력할 수도 있다.

default export는 import할 때도 기본적으로 default 키워드로 구분이 된다. (키워드 자체로는 선언할 수 없고 반드시 as 키워드를 통해서 이름을 붙여주어야한다.)

import { default as printerJS } from './printer.js';

console.log(printerJS.title); // CodePrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }
export default print; //하나의 대상만 export : Default Export
export { title as printerTitle, print, printArr }; //변수나 함수의 이름으로 여러 대상을 export 하는 방식 : Named Export


//조금 특별하게 import 사용하기
import {
  default as code,
  title as membersTitle,
  data as membersData
} from './members.js'

//에서 default as 부분을 생략하고 default 값에 붙여준 이름을 중괄호 밖으로 빼서 사용할 수 있다.
import code, {
  title as membersTitle,
  data as membersData
} from './members.js'

//import를 할 때 중괄호 여부로 Default Export와 Named Export를 구분할 수 있다.

다음과 같이 축약형 문법으로 import 할 수도 있다.

import * as printerJS from './printer.js'; //를
import printerJS from './printer.js'; //로 축약할 수 있다.

console.log(printerJS.title); // CodePrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }

사용할 때 객체 형태로 사용할 수가 있게 되는데 import 부분의 코드를 간결하게 쓸 수 있고 객체 형태로 값을 활용하기 때문에 다른 모듈과 이름 중복을 피할 수 있다는 장점이 있긴 하지만 객체 형식으로 활용하면 매번 점 표기법으로 프로퍼티에 접근해야 하고, 모듈 파일을 불러온 입장에서 각 대상의 이름을 변경할 수 없다는 단점도 있다.

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

얕고(Shallow), 깊은(Deep) 복사(Copy)  (1) 2024.12.20
==와 ===의 차이  (0) 2024.12.20
배열 메소드 Ⅵ (map, set)  (0) 2024.12.19
배열 메소드 Ⅴ (sort, reverse)  (0) 2024.12.19
배열 메소드 Ⅳ (reduce)  (0) 2024.12.19

Map

Map은 이름이 있는 데이터를 저장한다는 점에서 객체와 비슷하지만, 할당연산자를 통해 값을 추가하고, 점 표기법이나 대괄호 표기법으로 접근하는 일반 객체와 다르게 Map은 메소드를 통해서 값을 추가하거나 접근할 수 있다.
new 키워드를 통해서 Map을 만들 수 있고 아래와 같은 메소드를 통해 Map 안의 여러 값들을 다룰 수 있다.

  • map.set(key, value): key를 이용해 value를 추가하는 메소드.
  • map.get(key): key에 해당하는 값을 얻는 메소드. key가 존재하지 않으면 undefined를 반환.
  • map.has(key): key가 존재하면 true, 존재하지 않으면 false를 반환하는 메소드.
  • map.delete(key): key에 해당하는 값을 삭제하는 메소드.
  • map.clear(): Map 안의 모든 요소를 제거하는 메소드.
  • map.size: 요소의 개수를 반환하는 프로퍼티. (메소드가 아닌 점 주의! 배열의 length 프로퍼티와 같은 역할)
// Map 생성
const zoo = new Map();

// set 메소드
zoo.set('title', '문자열 key');
zoo.set(2017, '숫자형 key');
zoo.set(true, '불린형 key');

// get 메소드
console.log(zoo.get(2017)); // 숫자형 key
console.log(zoo.get(true)); // 불린형 key
console.log(zoo.get('title')); // 문자열 key

// has 메소드
console.log(zoo.has('title')); // true
console.log(zoo.has('name')); // false

// size 프로퍼티
console.log(zoo.size); // 3

// delete 메소드
zoo.delete(true);
console.log(zoo.get(true)); // undefined
console.log(zoo.size); // 2

// clear 메소드
zoo.clear();
console.log(zoo.get(2017)); // undefined
console.log(zoo.size); // 0

문자열과 심볼 값만 key(프로퍼티 네임)로 사용할 수 있는 일반 객체와는 다르게 Map 객체는 메소드를 통해 값을 다루기 때문에 다양한 자료형을 key로 활용할 수 있다는 장점이 있다.

Set

Set은 여러 개의 값을 순서대로 저장한다는 점에서 배열과 비슷하지만, 배열의 메소드는 활용할 수 없고 Map과 비슷하게 Set만의 메소드를 통해서 값을 다루는 특징이 있다.
Map과 마찬가지로 new 키워드로 Set을 만들 수 있고 아래와 같은 메소드를 통해 Set 안의 여러 값들을 다룰 수 있다.

  • set.add(value): 값을 추가하는 메소드. (메소드를 호출한 자리에는 추가된 값을 가진 Set 자신을 반환.)
  • set.has(value): Set 안에 값이 존재하면 true, 아니면 false를 반환하는 메소드.
  • set.delete(value): 값을 제거하는 메소드. (메소드를 호출한 자리에는 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환.)
  • set.clear(): Set 안의 모든 요소를 제거하는 메소드.
  • set.size: 요소의 개수를 반환하는 프로퍼티. (메소드가 아닌 점 주의! 배열의 length 프로퍼티와 같은 역할)
// Set 생성
const members = new Set();

// add 메소드
members.add('영훈'); // Set(1) {"영훈"}
members.add('윤수'); // Set(2) {"영훈", "윤수"}
members.add('동욱'); // Set(3) {"영훈", "윤수", "동욱"}
members.add('태호'); // Set(4) {"영훈", "윤수", "동욱", "태호"}

// has 메소드
console.log(members.has('동욱')); // true
console.log(members.has('현승')); // false

// size 프로퍼티
console.log(members.size); // 4

// delete 메소드
members.delete('종훈'); // false
console.log(members.size); // 4
members.delete('태호'); // true
console.log(members.size); // 3

// clear 메소드
members.clear();
console.log(members.size); // 0

한가지 특이한 점은 일반 객체는 프로퍼티 네임으로, Map은 get메소드로, 그리고 배열은 index를 통해서 개별 값에 접근할 수 있었는데 Set에는 개별 값에 바로 접근하는 방법이 없다.

// Set 생성
const members = new Set();

// add 메소드
members.add('영훈'); // Set(1) {"영훈"}
members.add('윤수'); // Set(2) {"영훈", "윤수"}
members.add('동욱'); // Set(3) {"영훈", "윤수", "동욱"}
members.add('태호'); // Set(4) {"영훈", "윤수", "동욱", "태호"}

for (const member of members) {
  console.log(member); // 영훈, 윤수, 동욱, 태호가 순서대로 한 줄 씩 콘솔에 출력됨.
}

그래서 위 코드와 같이 반복문을 통해서 전체요소를 한꺼번에 다룰 때 반복되는 그 순간에 개별적으로 접근할 수가 있다.

이런 특징을 가지고도 Set이 유용하게 사용되는 경우가 있는데 바로, 중복을 허용하지 않는 값들을 모을 때다.

Set은 중복되는 값을 허용하지 않는 독특한 특징이 있는데 Set 객체에 요소를 추가할 때 이미 Set 객체 안에 있는 값(중복된 값)을 추가하려고 하면 그 값은 무시되는 특징이 있다. 처음 Set을 생성할 때 아규먼트로 배열을 전달할 수가 있는데, 이런 특징을 활용해서 배열 내에서 중복을 제거한 값들의 묶음을 만들 때 Set을 활용할 수 있다.

const numbers = [1, 3, 4, 3, 3, 3, 2, 1, 1, 1, 5, 5, 3, 2, 1, 4];
const uniqNumbers = new Set(numbers);

console.log(uniqNumbers); // Set(5) {1, 3, 4, 2, 5}

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

==와 ===의 차이  (0) 2024.12.20
자바스크립트 모듈  (0) 2024.12.19
배열 메소드 Ⅴ (sort, reverse)  (0) 2024.12.19
배열 메소드 Ⅳ (reduce)  (0) 2024.12.19
배열 메소드 Ⅲ (some, every)  (0) 2024.12.19

sort 메소드

배열에서 sort라는 메소드를 활용하면 배열을 정렬할 수 있다.
sort 메소드에 아무런 아규먼트도 전달하지 않을 때는 기본적으로 유니코드에 정의된 문자열 순서에 따라 정렬된다.

const letters = ['D', 'C', 'E', 'B', 'A'];
const numbers = [1, 10, 4, 21, 36000];

letters.sort();
numbers.sort();

console.log(letters); // (5) ["A", "B", "C", "D", "E"]
console.log(numbers); // (5) [1, 10, 21, 36000, 4]

그렇기 때문에 numbers에 sort 메소드를 사용한 것 처럼, 숫자를 정렬할 때는 우리가 상식적으로 이해하는 오름차순이나 내림차순 정렬이 되지 않는다. 그럴 땐 sort 메소드에 다음과 같은 콜백함수를 아규먼트로 작성해주면 되는데,

const numbers = [1, 10, 4, 21, 36000];

// 오름차순 정렬
numbers.sort((a, b) => a - b);
console.log(numbers); // (5) [1, 4, 10, 21, 36000]

// 내림차순 정렬
numbers.sort((a, b) => b - a);
console.log(numbers); // (5) [36000, 21, 10, 4, 1]

sort 메소드를 사용할 때 한 가지 주의해야될 부분은 메소드를 실행하는 원본 배열의 요소들을 정렬한다는 점이다. 그래서 한 번 정렬하고 나면 정렬하기 전의 순서로 다시 되돌릴 수 없으니까, 원본 배열의 순서가 필요하다면 미리 다른 변수에 복사해두는 것이 좋다.

reverse 메소드

reverse 메소드는 말 그대로 배열의 순서를 뒤집어 주는 메소드이다. reverse 메소드는 별도의 파라미터가 존재하지 않기 때문에 단순히 메소드를 호출해주기만 하면 배열의 순서가 뒤집히는데 sort 메소드와 마찬가지로 원본 배열의 요소들을 뒤집어 버린다는 점은 꼭 주의해야한다.

const letters = ['a', 'c', 'b'];
const numbers = [421, 721, 353];

letters.reverse();
numbers.reverse();

console.log(letters); // (3) ["b", "c", "a"]
console.log(numbers); // (3) [353, 721, 421]

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

자바스크립트 모듈  (0) 2024.12.19
배열 메소드 Ⅵ (map, set)  (0) 2024.12.19
배열 메소드 Ⅳ (reduce)  (0) 2024.12.19
배열 메소드 Ⅲ (some, every)  (0) 2024.12.19
배열 메소드 Ⅱ (filter, find)  (0) 2024.12.19

reduce 메소드

reduce 메소드는 누적값을 계산할 때 활용하는 조금 독특한 메소드인데 일반적으로 두 개의 파라미터를 활용한다.

첫 번째를 제외한 세 파라미터는 다른 메소드들의 콜백함수와 역할이 똑같다. 배열의 요소가 전달되고(el), 요소의 인덱스가 전달되고(i), 메소드를 호출한 배열이 전달(arr)된다. reduce 메소드에서 특별한 첫번째 파라미터는 Accumulator(누산기)라고 한다. (벌써 익숙하지 않은 느낌) 줄여서 acc라고도 작성하는데, 이 파라미터는 매번 콜백 함수가 반복해서 동작할 때 직전에 동작한 콜백 함수가 리턴한 값을 전달받는 파라미터이다. 그래서 reduce 메소드에 활용되는 콜백함수는 그 다음 콜백함수에 acc로 전달할 값을 리턴해주게 되는데, 매 반복 때마다 콜백 함수의 리턴값을 다음 콜백 함수의 acc로 전달해주면서 결국에는 마지막에 실행되는 콜백 함수의 리턴값이 reude 메소드의 최종 리턴값이 되는 것이다. 

그런데, 위 설명대로라면 제일 처음 실행되는 콜백함수는 acc에 전달받을 값이 없다. 그래서 첫번째 콜백 함수가 동작할 때 acc의 값을 reduce 메소드의 두번째 아규먼트로 전달해준다.

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

// reduce
const sumAll = numbers.reduce((accumulator, element, index, array) => {
  return accumulator + element; //nextAccValue(다음 누산기)
}, 0); //initialAccValue(초기값)

console.log(sumAll); // 10

조금 복잡하지만 여기까지가 reduce 메소드의 기본적 문법이다.

실제로 작성하는 코드를 작성해보자.

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

const sumAll = numbers.reduce((acc, el, i) => {
  console.log(`${i}번 index의 요소로 콜백함수가 동작중입니다.`);
  console.log('acc:', acc);
  console.log('el:', el);
  console.log('----------');
  
  return acc + el;
}, 0);

console.log('sumAll:', sumAll);

reduce 메소드의 동작 원리를 가장 간단하게 확인하는 방법은 각 배열의 값들을 합산해보는 것이다. 인덱스와 함께 앞에 있는 두 파라미터(acc, el)도 출력하면서 각 반복동작을 구분하기 위해 구분선(-----)도 출력해주고 있다. 그리고 마지막은 두 값을 합한 결과(acc+el)를 리턴하고 있다. 메소드를 실행할 때 초기값을 0으로 작성해놓았기 때문에 처음 0번 인덱스의 요소로 콜백 함수가 동작할 때는 acc의 값이 0이고 요소의 값은 1이 된다.

결과

그리고 나서 콜백 함수가 이 두 값에 합(acc+el)을 리턴하기 때문에 0+1이 다음 콜백 함수에 acc로 전달되는 것이다. 이 과정이 계속 반복되면서 1+2가 다음 acc로 전달되고, 그 다음 3과 더해져서 결국 마지막 콜백함수가 실행될 때는 6+4가 리턴되면서 sumAll이라는 변수에 할당된 것이다.

+사실 reduce 메소드의 두번째 아규먼트(초기값)는 선택사항이다. 이 값을 작성해주면 가장 처음에 동작하는 콜백함수의 acc로 전달이 되지만 만약 생략을 하더라도 에러가 발생하지 않고 배열의 0번 인덱스에 있는 요소의 값이 첫번째 실행되는 콜백함수의 acc로 전달돼서 결과값도 동일하게 실행되긴 한다.

초기값을 지우고 실행해본 코드

하지만 처음에 실행되는 콜백 함수가 0번 인덱스가 아니라 1번 인덱스로 시작됐다. 배열의 요소는 총 네 개인데 콜백함수는 총 세번만 동작한 것이다. 지금은 단순히 두 파라미터의 합을 리턴하기 때문에 최종적인 리턴값이 똑같이 나왔지만 만약 조금 더 복잡한 동작을 했다면 예상치 못한 결과가 나타났을 수도 있다. 그래서 reduce 메소드를 사용할 땐 가급적 초기값을 명시해 주는것이 더 안전하게 사용 할 수 있는 방법이다.

some 메소드

some 메소드는 배열 안에 콜백함수가 리턴하는 조건을 만족하는 요소가 1개 이상 있는지를 확인하는 메소드이다. 
배열을 반복하면서 모든 요소가 콜백함수가 리턴하는 조건을 만족하지 않는다면 false를 리턴하고, 배열을 반복하면서 콜백함수가 리턴하는 조건을 만족하는 요소가 등장한다면 바로 true를 리턴하고 반복을 종료한다.

const numbers = [1, 3, 5, 7, 9];

// some: 조건을 만족하는 요소가 1개 이상 있는지
const someReturn = numbers.some((element, index, array) => {
  console.log(index); // 콘솔에는 0, 1, 2, 3까지만 출력됨.
  return element > 5;
});

console.log(someReturn); 
//출력
0
1
2
3
true;

 

every 메소드

every 메소드는 배열 안에 콜백 함수가 리턴하는 조건을 만족하지 않는 요소가 1개 이상 있는지를 확인하는 메소드이다.
배열을 반복하면서 모든 요소가 콜백함수가 리턴하는 조건을 만족한다면 true를 리턴하고, 배열을 반복하면서 콜백함수가 리턴하는 조건을 만족하지 않는 요소가 등장한다면 바로 false를 리턴하고 반복을 종료한다.

const numbers = [1, 3, 5, 7, 9];

// every: 조건을 만족하지 않는 요소가 1개 이상 있는지
const everyReturn = numbers.every((element, index, array) => {
  console.log(index); // 콘솔에는 0까지만 출력됨.
  return element > 5;
});

console.log(everyReturn); 
//출력
0
false;

+ 메소드를 호출하는 배열이 빈 배열일 경우 콜백함수를 실행하지도 않고 바로 some은 false, every는 true를 리턴한다.

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

배열 메소드 Ⅴ (sort, reverse)  (0) 2024.12.19
배열 메소드 Ⅳ (reduce)  (0) 2024.12.19
배열 메소드 Ⅱ (filter, find)  (0) 2024.12.19
배열 메소드 Ⅰ (forEach, map)  (0) 2024.12.19
에러와 에러 객체  (1) 2024.12.18

filter 메소드

filter 메소드는 배열의 요소를 하나씩 살펴보면서 콜백함수가 리턴하는 조건과 일치하는 요소만 모아서 새로운 배열을 리턴하는 메소드이다. (이름 그대로 어떤 조건에 따라 필터링된 새로운 배열을 얻고자 할 때 활용할 수 있다.)

//filter
const devices = [
  {name: 'GalaxyNote', brand: 'Samsung'},
  {name: 'MacbookPro', brand: 'Apple'},
  {name: 'Gram', brand: 'LG'},
  {name: 'SurfacePro', brand: 'Microsoft'},
  {name: 'ZenBook', brand: 'Asus'},
  {name: 'MacbookAir', brand: 'Apple'},
];

//map메소드와 비슷해보인다, 아규먼트가 되는 콜백함수 구조도 비슷하다.
const apples = devices.filter((element, index, array) => {
  return element.brand === 'Apple';
});

console.log(apples); // (2) [{name: "MacbookPro", brand: "Apple"}, {name: "MacbookAir", brand: "Apple"}]

 

filter가 map과 다른점: 리턴문으로 어떤 값을 전달하는게 아니라 true 혹은 false로 평가되는 조건식을 리턴해준다.
그러면 이 메소드를 호출한 배열을 반복하면서 콜백함수가 리턴하는 조건식이 true가 되는 요소만 모아서 새로운 배열을 리턴해준다.

그런데, filter 메소드는 항상 리턴값이 배열이기 때문에 아래 코드처럼 name 프로퍼티 값이 고유한 값을 활용해서 하나만 있는 요소를 필터링하더라도 결국에는 하나의 요소를 가진 배열을 리턴해준다.

const devices = [
  {name: 'GalaxyNote', brand: 'Samsung'},
  {name: 'MacbookPro', brand: 'Apple'},
  {name: 'Gram', brand: 'LG'},
  {name: 'SurfacePro', brand: 'Microsoft'},
  {name: 'ZenBook', brand: 'Asus'},
  {name: 'MacbookAir', brand: 'Apple'},
];

const myLaptop = devices.filter((element, index, array) => {
  return element.name === 'Gram';
});

console.log(myLaptop);
//출력되는 값
[{...}]

//그래서 spread 구문을 이용해서 배열을 벗겨내는 작업을 해야한다.
console.log(...myLaptop);
//출력되는 값
{name: "Gram", brand: "LG"}

find 메소드

find 메소드는 filter 메소드와 비슷하게 동작하지만, 배열의 요소들을 반복하는 중에 콜백함수가 리턴하는 조건과 일치하는 가장 첫번째 요소를 리턴하고 반복을 종료하는 메소드이다.

const devices = [
  {name: 'GalaxyNote', brand: 'Samsung'},
  {name: 'MacbookPro', brand: 'Apple'},
  {name: 'Gram', brand: 'LG'},
  {name: 'SurfacePro', brand: 'Microsoft'},
  {name: 'ZenBook', brand: 'Asus'},
  {name: 'MacbookAir', brand: 'Apple'},
];

const myLaptop = devices.find((element, index, array) => {
  console.log(index); // 콘솔에는 0, 1, 2까지만 출력됨.
  return element.name === 'Gram';
});

console.log(myLaptop); // {name: "Gram", brand: "LG"}

 

filter 와 find의 차이점

  1. filter는 리턴값이 배열이고, find는 값이다.
  2. 같은 배열에서 메소드를 호출하더라도 반복하는 횟수의 차이가 있을 수 있다. (프로그램의 효율 측면에서 중요! find의 경우 조건을 만족하는 하나의 값만 찾기 때문에 그 값을 찾는 순간 반복이 종료된다.)
const devices = [
  {name: 'GalaxyNote', brand: 'Samsung'},
  {name: 'MacbookPro', brand: 'Apple'},
  {name: 'Gram', brand: 'LG'},
  {name: 'SurfacePro', brand: 'Microsoft'},
  {name: 'ZenBook', brand: 'Asus'},
  {name: 'MacbookAir', brand: 'Apple'},
];

const apples = devices.filter((el, i) => {
  console.log(i);
  return el.brand === 'Apple';
});

console.log(apples);
//출력되는 값
0
1
2
3
4
5
(2) {...}, {...}

const myLaptop = devices.find((el, i) => {
  console.log(i); 
  return element.name === 'Apple';
});

console.log(myLaptop);
//출력되는 값
0
1
{name: "MacbookPro", brand: "Apple"}

filter 메소드는 조건을 만족하는 모든 값을 모아야 하기 때문에 배열의 모든 인덱스가 콘솔에 다 출력되었고, find 메소드는 조건을 만족하는 하나의 요소만 찾으면 되니까 단 두번의 인덱스만 콘솔에 출력되면서 콜백함수가 두번만 실행되고 종료되었다는걸 확인 할 수 있다.

+ 상황에 따라서 존재하지 않는 요소를 찾는다거나 가장 마지막에 위치해 있는 요소를 찾는다면 결국엔 filter 메소드와 반복횟수가 같아질 수 있다. 존재하지 않는요소를 찾으면 처음부터 마지막 인덱스까지 돈 후 undefined 값이 출력된다.

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

배열 메소드 Ⅳ (reduce)  (0) 2024.12.19
배열 메소드 Ⅲ (some, every)  (0) 2024.12.19
배열 메소드 Ⅰ (forEach, map)  (0) 2024.12.19
에러와 에러 객체  (1) 2024.12.18
구조 분해 Destructuring  (0) 2024.12.17

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

ES2015 이후부터는 자바스크립트에서 변수나 함수를 활용해서 프로퍼티를 만들 때 프로퍼티 네임과 변수나 함수 이름이 같다면 축약해서 사용할 수 있다.

function sayHi() {
  console.log('Hi!');
}

const title = 'code';
const birth = 2000;
const job = '인플루언서';

//때로는 객체를 만들 때 변수에 할당된 값을 활용해서 프로퍼티를 만들 수도 있다.
const user = {
  title: title, //활용할 변수의 이름과 프로퍼티 네임이 똑같다면
  birth, //이렇게 하나만 작성하는 표현이 가능하다.
  job, 
  sayHi,
};

console.log(user); // {title: "code", birth: 2000, job: "인플루언서", sayHi: ƒ}

그리고 메소드를 작성할 때도 다음과 같이 function 키워드를 생략할 수가 있다.

//getFullName: function() {
//    return `${this.firstName} ${this.lastName}`;
//  },
//: function을 생략할 수 있다.

const user = {
  firstName: 'Tess',
  lastName: 'Jang',
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  },
};

console.log(user.getFullName()); // Tess Jang

뿐만아니라 아래 코드와 같이 표현식을 대괄호로 감싸주게 되면, 표현식의 값을 프로퍼티 네임으로 쓸 수 있다.

//계산된 속성명(computed property name)
const usr = {
  [표현식]: 값,
};

const propertyName = 'birth';
const getJob = () => 'job';

const codeTest = {
  ['topic' + 'name']: 'Modern JavaScript',
  [propertyName]: 1998, //변수에 담긴 값을 사용하거나
  [getJob()]: '프로그래밍 강사' //함수의 리턴값을 사용할 수도 있다.
};

console.log(codeTest); //{topicName: "Modern JavaScript", birth: 1998, job: "프로그래밍 강사"}

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

에러와 에러 객체  (1) 2024.12.18
구조 분해 Destructuring  (0) 2024.12.17
Spread 구문  (0) 2024.12.16
조건부 연산자(삼항 연산자)  (0) 2024.12.16
함수  (0) 2024.12.15

+ Recent posts