WeniVooks

검색

JavaScript 에센셜

객체타입과 배열

1. 객체타입 (Object Types)

객체 타입은 여러 속성들의 집합을 나타냅니다. 여러 값들을 저장할 수 있습니다. 원시 타입과 달리 객체 타입은 값을 변경할 수 있습니다. 객체 타입은 값이 아닌 실제 값이 저장되어 있는 위치의 주소를 가리키는 참조(reference)를 저장합니다.

let user = { age: 30, name:"licat" } user.age = 20; console.log(user.age);

2. 배열 (Array)

배열은 데이터를 순서대로 저장하는 객체입니다. 하나의 데이터를 표현하는 원시타입과 달리 여러 개의 데이터를 한 변수에 저장할 수 있기 때문에 데이터를 추가하거나 제거, 정렬, 검색 등 다양한 작업을 수행할 수 있습니다.

2.1. 배열의 특징
2.1.1. 배열 생성

배열은 빈 배열로 생성하거나 요소가 포함된 배열로 생성할 수 있습니다. 배열은 대괄호([])를 사용하여 생성하며, 배열 안에 요소를 쉼표(,)로 구분하여 나열합니다. 또는 Array 생성자 함수를 사용하여 배열을 생성할 수도 있습니다. 배열은 다양한 타입의 데이터를 포함할 수 있습니다.
또한 배열은 const로 선언하는 것이 일반적입니다. 그 이유는 const로 선언한 것이 '배열의 요소를 바꿀 수 없는 것'이 아니라 '배열의 주소값을 바꿀 수 없는 것' 이기 때문입니다. 아래 예제에서는 arr2가 [1, 2, 3]을 가리키고 있다는 사실을 변경시키지 않겠다는 의미입니다.

const arr1 = []; const arr2 = [1, 2, 3]; const arr3 = new Array(4, 5, 6); const arr4 = new Array(3); console.log(arr1) console.log(arr2) console.log(arr3) console.log(arr4)
2.1.2. 배열 요소 접근

배열은 숫자를 사용하여 값에 접근할 수 있습니다. 숫자는 값의 순서를 의미하며, 이 순서를 인덱스(index)라고 부르고 배열 안에 존재하는 값을 원소(elements)라고 부릅니다.

const arr = [10, 20, 30]; // 배열 안의 원소에 접근하기 위해서는 인덱스 번호를 이용합니다. console.log(arr[0]); // 10 console.log(arr[1]); // 20 console.log(arr[2]); // 30 console.log(arr[3]); // ??
2.1.3. 배열의 길이

배열은 길이를 나타내는 length 프로퍼티를 가지고 있습니다. 문자열에서도 사용했던 length 프로퍼티와 같은 역할을 합니다.

const arr = [10, 20, 30, 40, 50]; console.log(arr.length); // 5
2.1.4.다차원 배열

배열은 배열 안에 다른 배열을 포함할 수 있습니다. 이러한 배열을 다차원 배열이라고 합니다.

const arr = [ [1, 2], [3, 4], [5, 6], ]; console.log(arr[0][0]); console.log(arr[2][1]);

2차원 배열은 행렬이라고 불리며, 3차원 이상의 배열도 만들 수 있습니다.

const arr = [ [ [1, 2], [3, 4], ], [ [5, 6], [7, 8], ], ]; console.log(arr[0][1][0]); console.log(arr[1][0][1]);
2.2. 배열의 메서드

배열의 메서드는 배열을 다루는데 유용한 기능들입니다. 배열의 메서드는 배열의 요소를 추가하거나 제거, 정렬, 검색 등 다양한 작업을 수행할 수 있습니다. 여기서는 실무에서 주로 사용되는 배열의 메서드를 알아보겠습니다.

2.2.1. push()와 pop()

push() 메서드는 배열의 끝에 요소를 추가하고 길이를 반환합니다. pop() 메서드는 배열의 마지막 요소를 꺼내어 반환합니다. 꺼낸 요소는 배열에서 제외됩니다.

const arr = [1, 2, 3]; arr.push(4); console.log(arr); arr.pop(); console.log(arr);

여기서 pop() 메서드는 배열의 마지막 요소를 꺼내어 반환하는데 이때 꺼낸 요소를 아래와 같이 활용하기도 합니다.

const arr = [1, 2, 3]; const last = arr.pop(); console.log(last);
2.2.2. shift()와 unshift()

shift() 메서드는 배열에서 첫 번째 요소를 꺼내어 반환합니다. unshift() 메서드는 배열의 첫 번째 요소로 새로운 요소를 추가합니다.

const arr = ['사과', '바나나', '수박']; arr.shift(); console.log(arr); arr.unshift('오이', '배'); console.log(arr);

shift() 메서드는 pop()과 마찬가지로 꺼낸 요소를 반환합니다.

const arr = ['사과', '바나나', '수박']; const first = arr.shift(); console.log(first);
2.2.3. splice()

splice() 메서드는 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 배열의 내용을 변경합니다. 변경을 시작할 인덱스와 변경할 요소의 개수를 지정하고, 추가할 요소가 있다면 추가할 요소를 쉼표로 구분하여 인자로 전달합니다. 추가할 요소가 없다면 요소를 삭제만 합니다.

const arr = [1, 2, 3, 4, 5]; // 삭제 arr.splice(1, 2); console.log(arr); // 교체 arr.splice(1, 1, 'a', 'b'); console.log(arr); // 추가 arr.splice(1, 0, 'c', 'd'); console.log(arr);
2.2.4. slice()

slice() 메서드는 배열에서 요소들을 추출하여 새로운 배열로 반환합니다. 시작 인덱스와 종료 인덱스를 인자로 전달받습니다. 이 때, 종료 인덱스는 포함하지 않고, 바로 이전 요소까지 추출합니다. 종료 인덱스를 생략하거나 배열의 길이보다 큰 경우 배열의 끝까지 추출합니다. 원본 배열을 변경하지 않고 새로운 배열을 반환합니다.

const arr = ['apple', 'banana', 'cherry', 'durian', 'elderberry']; console.log(arr.slice(1, 4)); console.log(arr.slice(2)); // 배열 전체를 추출 console.log(arr.slice()); console.log(arr.slice(0, 10));

배열 복사 slice() 메서드를 사용할 때 인자를 생략하면 배열 전체를 복사할 수 있습니다. 이때, 새로운 배열을 반환하므로 원본 배열을 변경하지 않습니다. 이러한 특성을 이용하여 배열을 복사할 수 있습니다.

const arr = ['apple', 'banana', 'cherry']; const copy = arr.slice(); copy[0] = 'orange'; console.log(arr); console.log(copy);
2.2.5. sort()

sort() 메서드는 배열의 요소를 정렬하는데 사용됩니다. 메서드를 호출하면 배열을 변경하며, 정렬된 배열을 반환합니다.

const fruits = ['사과', '바나나', '수박', '딸기', '포도']; console.log(fruits.sort());

숫자를 정렬하려고 하면 의도와는 다르게 정렬될 수 있습니다.

const nums = [3, 1, 8, 6]; console.log(nums.sort()); const nums2 = [23, 5, 1000, 42]; console.log(nums2.sort());

숫자를 정렬하려고 하면 의도와는 다르게 정렬이 되는 이유는 정렬하기 전 원소를 문자열로 전환한 후에 유니코드 포인트의 순서대로 변환하기 때문입니다. 포인트는 문자에 부여한 고유한 16진수 숫자값입니다.

유니코드 포인트

숫자형 데이터 정렬의 이러한 단점을 해결하기 위해 비교 함수(compareFunction)를 사용할 수 있습니다. 비교 함수가 제공되면 원소의 순서는 비교 함수의 반환 값에 따라 정렬됩니다.

두 인자 a, b를 비교함수로 비교하여 비교함수의 반환값이 음수이면 a를 앞으로 위치하고, 0이면 위치를 변경하지 않고, 양수이면 b를 앞으로 위치합니다.

다음 코드의 비교 함수는 a - b를 반환하므로 오름차순으로 정렬됩니다. a가 b보다 크면, a - b는 양수가 되어 b가 a보다 앞으로 위치하고 b가 a보다 크면, a - b는 음수가 되어 a가 b보다 앞으로 위치합니다.

let numbers = [4, 2, 5, 1, 3]; numbers.sort((a, b) => a - b); console.log(numbers);

Tim sort 알고리즘
JavaScript 의 sort 메서드가 원소를 정렬하는 방법은 Tim sort 알고리즘이라고 합니다.(Tim Peters라는 이름의 개발자가 만들었습니다.) Tim sort는 합병 정렬(Merge sort)과 삽입 정렬(Insertion sort)의 아이디어를 결합한 알고리즘으로 매우 안정적으로 동작하기 때문에 파이썬과 자바 등 다른 언어에서도 널리 사용되는 방법입니다.

데이터를 여러 개의 덩어리로 분할하고, 이 덩어리들을 삽입 정렬 알고리즘으로 정렬한 다음, 합병 정렬 알고리즘을 이용해 정렬된 덩어리들을 하나로 합칩니다. 이렇게 분할하고 정렬하는 과정에서, 이미 정렬된 데이터를 인식하고 이를 활용함으로써 평균적인 수행 시간을 크게 줄일 수 있습니다.

Tim sort에 대해 알아보자
2.2.6. indexOf

indexOf는 배열에서 특정 요소를 찾아 인덱스를 반환합니다. 배열의 첫 번째 요소부터 검색하여 찾은 요소의 인덱스를 반환하며, 찾지 못하면 -1을 반환합니다.

const arr = [1, 2, 3, 4, 2, 3, 5, 6]; console.log(arr.indexOf(2)); console.log(arr.indexOf(5)); console.log(arr.indexOf(8));
2.2.7. includes

includes() 메서드는 배열에 특정 요소가 포함되어 있는지를 확인하여, 포함되어 있으면 true, 그렇지 않으면 false를 반환합니다. 이 메서드는 배열에서 특정 값을 찾을 때 유용합니다.

const arr = ['hello', 'world', 'weniv']; console.log(arr.includes('world')); console.log(arr.includes('licat'));
2.2.8. forEach()

forEach() 메서드는 배열의 각 요소에 대해 주어진 함수를 실행합니다. 이 때, 함수는 인자로 배열 요소, 인덱스를 받습니다. forEach() 메서드는 배열의 요소를 순환하면서 해당 요소를 함수로 전달하고, 이 함수가 각 요소에 대해 실행됩니다.

const 배열 = ['a', 'b', 'c'];
배열.forEach(요소마다_실행시킬_함수);
const 배열 = ['a', 'b', 'c'];
배열.forEach(요소마다_실행시킬_함수);

형태는 위와 같은 형태가 됩니다. 좀 더 이해하기 쉬운 예제를 살펴보겠습니다.

const arr = ['참외', '키위', '감귤']; function 함수(요소, 인덱스) { console.log(요소, 인덱스); } arr.forEach(함수);

다만 위와 같이 함수를 따로 선언하지 않고, 아래와 같이 함수를 forEach() 안에서 선언할 수도 있습니다.

const arr = ['참외', '키위', '감귤']; arr.forEach(function (item, index) { console.log(item, index); });

요즘은 화살표 함수를 좀 더 많이 사용합니다.

const arr = ['참외', '키위', '감귤']; arr.forEach((item, index) => { console.log(item, index); });

forEach() 메서드는 배열의 각 요소에 대해 특정 작업을 수행 할 때 사용됩니다. 예를 들어, 배열의 각 요소를 출력하거나, 배열의 요소를 이용하여 다른 배열을 만드는 등의 작업을 수행할 수 있습니다.

const fruits = ['딸기', '메론', '수박', '복숭아']; const breads = []; fruits.forEach((item) => { breads.push(item + '빵'); }); console.log(breads);
2.2.9. map()

map() 메서드는 배열의 각 요소에 대해 주어진 함수를 실행하고, 그 결과를 새로운 배열로 반환합니다.

const arr = [1, 2, 3]; const newArr = arr.map(function (item, index) { return item * index; }); console.log(newArr);

map() 메서드의 첫 번째 인자로는 배열의 각 요소를 처리할 함수를, 두번째는 요소의 인덱스를 전달합니다. 이 함수는 배열의 각 요소를 매개변수로 받아 처리한 후, 그 결과를 반환합니다.

forEach() 메서드와 비슷해 보이지만, map() 메서드는 새로운 배열을 반환한다는 점에서 차이가 있습니다. forEach() 메서드는 배열의 요소를 순회 돌고 싶을 때, map() 메서드는 return되는 값으로 새로운 배열을 만들 때 사용합니다.

아래처럼 배열 안에 객체에서 데이터를 뽑는 형태로도 사용합니다.

const data = [ { _id: '642ba3980785cecff3f39a8d', index: 0, age: 28, name: 'Annette Middleton', gender: 'female', company: 'KINETICA', }, { _id: '642ba398d0fed6e17f2f50c9', index: 1, age: 37, name: 'Kidd Roman', gender: 'male', company: 'AUSTECH', }, { _id: '642ba39827d809511d00dd8d', index: 2, age: 39, name: 'Best Ratliff', gender: 'male', company: 'PRISMATIC', }, ]; // const ages = data.map(item => item.age); const ages = data.map((item) => item['age']); console.log(ages);
2.2.10. for … of

for ... of는 반복 가능한(iterable)한 객체의 순회를 위한 기능입니다. 주로 배열, 문자열이나 앞으로 배우게될 Map, Set 등의 요소에 직접 접근해서 각 요소의 값을 처리하거나 조작할 때 사용합니다.

for (const 요소 of 배열) {
  // ...
}
for (const 요소 of 배열) {
  // ...
}
const fruits = ['사과', '바나나', '오렌지']; for (let fruit of fruits) { console.log(fruit); }
2.2.11. filter()

filter를 이용하면 특정 조건을 만족하는 요소들만 추출하여 새로운 배열을 반환할 수 있습니다. 인자로 전달받은 함수를 각 배열 요소에 대해 실행하고, 함수의 반환값이 true인 요소만 추출하여 새로운 배열을 만듭니다.

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const newArr = arr.filter(function (el) { return el % 2 === 0; // 요소가 짝수인 경우에만 true를 반환 }); console.log(newArr);
2.2.12. reduce

reduce는 이름에서 알 수 있듯이 배열의 요소를 하나로 줄이는(reduce) 역할을 합니다. 배열의 각 요소에 대해 함수를 실행하고, 그 결과를 누적(accumulate)하여 반환합니다. reduce 메서드는 배열의 각 요소에 대해 함수를 실행하고, 이전 계산 결과인 a(accumulator, 누적값)와 현재 요소인 c(currentValue)를 함수의 인자로 전달합니다. 두 번째 인자로 값을 전달하면 a의 초기값으로 사용됩니다.

const arr1 = [1, 2, 3, 4, 5]; let result = arr1.reduce((a, c) => { console.log(`a: ${a}, c:${c}`); return a + c; // 반환값이 다음 함수의 첫 번째 매개변수인 a로 전달됩니다 }, 0); console.log(result);

빈 배열의 경우 초기값이 없을 때 오류가 발생합니다. 초기값을 0으로 설정하면 오류가 발생하지 않습니다.

const arr = []; // arr.reduce((a, c) => a + c); // error arr.reduce((a, c) => a + c, 0);
2.2.13. join

join() 메서드는 배열의 모든 요소를 연결하여 하나의 문자열로 만듭니다. 이 메서드는 배열의 각 요소를 연결할 때 사용할 문자열을 인자로 받습니다. 인자를 생략하면 쉼표(,)로 연결됩니다.

const arr = ['hello', 'world', 'weniv']; console.log(arr.join('!')); // "hello!world!weniv" const fruits = ['apple', 'banana', 'cherry']; console.log(fruits.join()); // "apple,banana,cherry" - 기본 구분자인 쉼표(,)로 연결 const numbers = [010, 1111, 2222]; console.log(numbers.join('-')); // "8-1111-2222" const strNumbers = ['010', '1111', '2222']; console.log(strNumbers.join('-')); // "010-1111-2222"

join() 메서드는 배열의 요소를 문자열로 변환하여 연결합니다. 이 때 주의할 점은 0으로 시작하는 숫자는 8진수로 해석되어 문자열로 변환됩니다. 위의 코드에서 숫자 010은 8진수로 해석되어 문자열로 변환되므로, 8이 출력됩니다. 정확한 010이 필요한 경우에는 처음부터 문자열로 다루는 것이 가장 안전합니다.

8진수 리터럴(Octal literal)
8진수 리터럴은 숫자를 8진법으로 표현하는 방식을 나타냅니다. 자바스크립트에서는 숫자 앞에 0을 붙여 8진수를 표현했습니다. 예를 들어, 010은 8진수 10을 의미하고, 10진수로 나타내면 8이 됩니다. 이 표기법은 혼란을 줄 수 있기 때문에 ES6에서는 0o 접두사를 사용하며, 엄격 모드에서는 0으로만 시작하는 8진수 리터럴을 사용하면 오류가 발생합니다.

const num = 064; console.log(num); // 0b는 2진수, 0o는 8진수, 0x는 16진수를 나타냅니다. console.log(0b100); console.log(0o100); console.log(0x100);

위니북스 또한 엄격모드와 같은 실행환경을 제공하고 있기 때문에 오류가 발생합니다. 결과는 개발자 도구의 콘솔창에서 확인해주세요.

이러한 점을 유의하면서 join() 메서드를 사용하면 배열을 쉽게 문자열로 연결하여 변환할 수 있습니다.

2.2.14. concat

concat() 메서드는 두 개 이상의 배열을 합쳐 새로운 배열을 반환합니다. 이 메서드는 원본 배열을 변경하지 않고 새로운 배열을 반환합니다.

const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr3 = [7, 8, 9] const newArr = arr1.concat(arr2, arr3, 10, 11); console.log(newArr);

인자 없이 concat 메서드를 호출하면 얕은 복사가 됩니다.

const arr = [1, 2, 3, 4, 5]; const copy = arr.concat(); copy[0] = 0; console.log(arr); console.log(copy);
4장 객체타입4.2 객체