5. 자료구조와 자료형(2)
5.4 배열
배열은 특수한 형태의 객체로, 순서가 있는 자료를 저장하고 관리하는 용도에 최적화된 자료구조입니다.
- 선언 방법:
// 대괄호 (가장 많이 쓰이는 방법임) let arr = [item1, item2...]; // new Array (잘 쓰이지 않음) let arr = new Array(item1, item2...);
new Array(number)
을 호출하면 길이가number
인 배열이 만들어지는데, 이 때 요소는 비어있습니다.
length
프로퍼티는 배열의 길이를 나타내줍니다. 정확히는 숫자형 인덱스 중 가장 큰 값에 1을 더한 값입니다. 배열 메서드는 length 프로퍼티를 자동으로 조정해줍니다.length
값을 수동으로 줄이면 배열 끝이 잘립니다.(되돌릴 수 없음)다음 연산을 사용하면 배열을 데큐처럼 사용할 수 있습니다.
push(...items)
– items를 배열 끝에 더해줍니다.pop()
– 배열 끝 요소를 제거하고, 제거한 요소를 반환합니다.shift()
– 배열 처음 요소를 제거하고, 제거한 요소를 반환합니다.unshift(...items)
– items를 배열 처음에 더해줍니다.아래 방법을 사용하면 모든 요소를 대상으로 반복 작업을 할 수 있습니다.
for (let i=0; i<arr.length; i++)
– 가장 빠른 방법이고 오래된 브라우저와도 호환됩니다.for (let item of arr)
– 배열 요소에만 사용되는 모던한 문법입니다.(인덱스를 모름)for (let i in arr)
– 배열엔 절대 사용하지 마세요.
- 첫 번째 요소, 두 번째 요소, 세 번째 요소 등과 같이 순서가 있는 컬렉션이 필요할 때
배열
사용 가능
배열 선언
let arr = new Array();
let arr = [];
let fruits = ["사과", "오렌지", "자두"];
- 각 배열 요소엔 0부터 시작하는 숫자(인덱스)가 매겨져 있음
- 배열 내 특정 요소를 얻고 싶다면 대괄호 안에 순서를 나타내는 숫자인 인덱스를 넣어주면 됨
let fruits = ["사과", "오렌지", "자두"];
alert( fruits[0] ); // 사과
alert( fruits[1] ); // 오렌지
alert( fruits[2] ); // 자두
- length를 사용하면 배열에 담긴 요소가 몇 개인지 확인 가능
let fruits = ["사과", "오렌지", "자두"];
alert( fruits.length ); // 3
- 배열 요소의 자료형엔 제약이 없음
pop·push와 shift·unshift
큐(queue)
- 큐(queue)는 배열을 사용해 만들 수 있는 대표적인 자료구조로, 배열과 마찬가지로 순서가 있는 컬렉션을 저장하는 데 사용
push
– 맨 끝에 요소를 추가shift
– 제일 앞 요소를 꺼내 제거한 후 남아있는 요소들을 앞으로 밀어주어 두 번째 요소가 첫 번째 요소가 됨- 선입선출(First-In-First-Out, FIFO): 먼저 집어넣은 요소가 먼저 나옴
스택(stack)
- 스택은 이처럼 '한쪽 끝’에 요소를 더하거나 뺄 수 있게 해주는 자료구조
push
– 요소를 스택 끝에 집어넣음pop
– 스택 끝 요소를 추출
- 후입선출(Last-In-First-Out, LIFO): 스택을 사용하면 가장 나중에 집어넣은 요소가 먼저 나옴
데큐(deque)
- 처음이나 끝에 요소를 더하거나 빼주는 연산을 제공하는 자료구조를 컴퓨터 과학 분야에선 데큐(deque, Double Ended Queue)라고 부름
pop
: 배열 끝 요소를 제거하고, 제거한 요소를 반환push
: 배열 끝에 요소를 추가shift
: 배열 앞 요소를 제거하고, 제거한 요소를 반환unshift
: 배열 앞에 요소를 추가push
와unshift
는 요소 여러 개를 한 번에 더해줄 수도 있음
배열의 내부 동작 원리
- 배열은 특별한 종류의 객체임
- 배열 arr의 요소를 arr[0]처럼 대괄호를 사용해 접근하는 방식은 객체 문법에서 옴(다만 배열은 키가 숫자라는 점만 다름)
- 배열은 객체와 마찬가지로 참조를 통해 복사됨
- 배열을 '순서가 있는 자료의 컬렉션’처럼 다루지 않고 일반 객체처럼 다루면 배열 관련 연산을 더 빠르게 해주는 최적화 기법들이 제대로 동작하지 않음
성능
push
와pop
은 빠르지만shift
와unshift
는 느림
shift
나unshift
가 느린 이유는 추가하거나 제거한 요소 이외의 모든 요소를 이동시켜야하기 때문(index
재배정)
반복문
for
let arr = ["사과", "오렌지", "배"];
for (let i = 0; i < arr.length; i++) {
alert( arr[i] );
}
for...of
현재 요소의 인덱스는 얻을 수 없고 값만 얻을 수 있음
let fruits = ["사과", "오렌지", "자두"];
// 배열 요소를 대상으로 반복 작업을 수행
for (let fruit of fruits) {
alert( fruit );
}
for...in
(🚨 배열에 사용을 지양)
‘필요 없는’ 프로퍼티들이 문제를 일으킬 가능성이 있기 때문에 배열에for...in
사용을 지양
‘length’ 프로퍼티
- 배열에 무언가 조작을 가하면 length 프로퍼티가 자동으로 갱신됨
- length 프로퍼티는 배열 내 요소의 개수가 아니라 가장 큰 인덱스에 1을 더한 값(
index
가 0부터 시작하기 때문) - length의 값을 수동으로 증가시키면 아무 일도 일어나지 않지만 값을 감소시키면 배열이 잘림
- 짧아진 배열은 다시 되돌릴 수 없음
- 배열 비우기:
arr.length = 0;
new Array()
let arr = new Array("사과", "배", "기타");
- 숫자형 인수 하나를 넣어서
new Array
를 호출하면 배열이 만들어지는데, 이 배열엔 요소가 없는 반면 길이는 인수와 같아짐 new Array(number)
를 이용해 만든 배열의 요소는 모두undefined
다차원 배열
- 다차원 배열(multidimensional array): 배열의 요소로 배열이 있을 경우
- 다차원 배열은 행렬을 저장하는 용도로 쓰임
toString
- 배열엔 toString 메서드가 구현되어 있어 이를 호출하면 요소를 쉼표로 구분한 문자열이 반환됨
5.5 배열과 메서드
요약
요소를 더하거나 지우기
- push(...items) – 맨 끝에 요소 추가하기
- pop() – 맨 끝 요소 추출하기
- shift() – 첫 요소 추출하기
- unshift(...items) – 맨 앞에 요소 추가하기
- splice(pos, deleteCount, ...items) – pos부터 deleteCount개의 요소를 지우고, items 추가하기
- slice(start, end) – start부터 end 바로 앞까지의 요소를 복사해 새로운 배열을 만듦
- concat(...items) – 배열의 모든 요소를 복사하고 items를 추가해 새로운 배열을 만든 후 이를 반환함. items가 배열이면 이 배열의 인수를 기존 배열에 더해줌
원하는 요소 찾기
- indexOf/lastIndexOf(item, pos) – pos부터 원하는 item을 찾음. 찾게 되면 해당 요소의 인덱스를, 아니면 -1을 반환함
- includes(value) – 배열에 value가 있으면 true를, 그렇지 않으면 false를 반환함
- find/filter(func) – func의 반환 값을 true로 만드는 첫 번째/전체 요소를 반환함
- findIndex는 find와 유사함. 다만 요소 대신 인덱스를 반환함
배열 전체 순회하기
- forEach(func) – 모든 요소에 func을 호출함. 결과는 반환되지 않음
배열 변형하기
- map(func) – 모든 요소에 func을 호출하고, 반환된 결과를 가지고 새로운 배열을 만듦
- sort(func) – 배열을 정렬하고 정렬된 배열을 반환함
- reverse() – 배열을 뒤집어 반환함
- split/join – 문자열을 배열로, 배열을 문자열로 변환함
reduce(func, initial) – 요소를 차례로 돌면서 func을 호출함. 반환값은 다음 함수 호출에 전달함. 최종적으로 하나의 값이 도출됨기타
- Array.isArray(arr) – arr이 배열인지 여부를 판단함
- some(fn) - 모든 요소를 대상으로 함수를 호출하고 함수의 반환 값을 true로 만드는 요소가 하나라도 있는지 여부를 확인
- every(fn) - 모든 요소를 대상으로 함수를 호출하고 함수의 반환 값을 true로 만드는지 여부를 확인
- fill(value, start, end)은 start부터 end까지 value를 채워 넣습니다.
- copyWithin(target, start, end)은 start부터 end까지 요소를 복사하고, 복사한 요소를 target에 붙여넣습니다. 기존 요소가 있다면 덮어씁니다.
🚨 sort, reverse, splice는 기존 배열을 변형시킨다는 점에 주의하시기 바랍니다.
요소 추가·제거 메서드
splice
arr.splice(index[, deleteCount, elem1, ..., elemN])
- 첫 번째 매개변수는 조작을 가할 첫 번째 요소를 가리키는 인덱스(index)
- 두 번째 매개변수는 deleteCount로, 제거하고자 하는 요소의 개수
- elem1, ..., elemN은 배열에 추가할 요소
- 삭제:
arr.splice(1, 1);
인덱스 1부터 요소 한 개를 제거 - 교체:
arr.splice(0, 3, "Let's", "dance");
처음(0) 세 개(3)의 요소를 지우고, 이 자리를 다른 요소로 대체 - 추가:
splice
메서드의deleteCount
를0
으로 설정하면 요소를 제거하지 않으면서 새로운 요소를 추가 가능 - splice는 삭제된 요소로 구성된 배열을 반환
delete
연산자
delete obj.key
는key
를 이용해 해당 키에 상응하는 값을 지우기 때문에 삭제된 요소가 만든 빈 공간을 나머지 요소들이 자동으로 채우지 않음- 즉, 배열의 길이는 그대로임
slice
arr.slice([start], [end])
start
인덱스부터 (end
를 제외한)end
인덱스까지의 요소를 복사한 새로운 배열을 반환start
와end
는 둘 다 음수일 수 있는데 이땐, 배열 끝에서부터의 요소 개수를 의미함
배열 복사
arr.slice()
는 인수를 하나도 넘기지 않고 호출하여 arr의 복사본을 만들 수 있음
concat
arr.concat
은 기존 배열의 요소를 사용해 새로운 배열을 만들거나 기존 배열에 요소를 추가하고자 할 때 사용arr.concat(arg1, arg2...)
- 메서드를 호출하면 arr에 속한 모든 요소와 arg1, arg2 등에 속한 모든 요소를 한데 모은 새로운 배열이 반환됨
- 인수 argN가 배열일 경우 배열의 모든 요소가 복사되고 그렇지 않은 경우(단순 값인 경우)는 인수가 그대로 복사됨
- concat 메서드는 제공받은 배열의 요소를 복사해 활용함
- 객체가 인자로 넘어오면 (배열처럼 보이는 유사 배열 객체이더라도) 객체는 분해되지 않고 통으로 복사되어 더해짐
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1
};
alert( arr.concat(arrayLike) ); // 1,2,[object Object]
forEach로 반복작업 하기
arr.forEach
는 주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해줌- 인수로 넘겨준 함수의 반환값은 무시됨
arr.forEach(function(item, index, array) {
// 요소에 무언가를 할 수 있습니다.
});
배열 탐색하기
indexOf, lastIndexOf와 includes
arr.indexOf(item, from)
는 인덱스from
부터 시작해item(요소)
을 찾는데, 요소를 발견하면 해당 요소의 인덱스를 반환하고, 발견하지 못했으면-1
을 반환함arr.lastIndexOf(item, from)
는 위 메서드와 동일한 기능을 하는데, 검색을 끝에서부터 시작한다는 점만 다름arr.includes(item, from)
는 인덱스from
부터 시작해item
이 있는지를 검색하는데, 해당하는 요소를 발견하면true
를 반환함- 🚨 세 메서드 모두 완전 항등 연산자
===
을 사용한다는 점에 유의(false
를 검색하면 정확히false
만을 검색하지, 0을 검색하진 않음) includes
는NaN
도 제대로 처리할 수 있음
find와 findIndex
arr.find(fn)
: 객체로 이루어진 배열에서 특정 조건에 부합하는 객체를 배열 내에서 찾음
let result = arr.find(function(item, index, array) {
// true가 반환되면 반복이 멈추고 해당 요소를 반환
// 조건에 해당하는 요소가 없으면 undefined를 반환
});
- 예시: 배열 내에서
id==1
조건을 충족하는 객체find
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
let user = users.find(item => item.id == 1);
alert(user.name); // John
arr.findIndex
는find
와 동일한 일을 하나, 조건에 맞는 요소를 반환하는 대신 해당 요소의 인덱스를 반환(조건에 맞는 요소가 없으면-1
이 반환)
filter
- 함수의 반환 값을 true로 만드는 단 하나의 요소를 찾는
find
와 달리 조건을 충족하는 요소가 여러 개라면arr.filter(fn)
를 사용 filter
는find
와 문법이 유사하지만, 조건에 맞는 요소 전체를 담은 배열을 반환함
let results = arr.filter(function(item, index, array) {
// 조건을 충족하는 요소는 results에 순차적으로 더해짐
// 조건을 충족하는 요소가 하나도 없으면 빈 배열을 반환
});
배열을 변형하는 메서드
map
arr.map
은 유용성과 사용 빈도가 아주 높은 메서드map
은 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 배열로 반환해줌
let result = arr.map(function(item, index, array) {
// 요소 대신 새로운 값을 반환
// 기존 arr 배열에는 변화가 없음
});
sort(fn)
- arr.sort()는 배열의 요소를 정렬해줌
- 🚨 배열 자체가 변경됨
- 요소는 문자열로 취급되어 재정렬됨(사전편집 순)
let arr = [ 1, 2, 15 ];
arr.sort(); // arr 내부가 재정렬됨
alert( arr ); // 1, 15, 2
- 새로운 정렬 기준을 만들려면 함수를 넘겨줘야함
- 오름차순 예시:
let arr = [ 1, 2, 15 ];
function compare(a, b) {
if (a > b) return 1; // 첫 번째 값이 두 번째 값보다 큰 경우
if (a == b) return 0; // 두 값이 같은 경우
if (a < b) return -1; // 첫 번째 값이 두 번째 값보다 작은 경우
}
arr.sort(compare);
// 리팩토링(1)
arr.sort(function(a, b) { return a - b; });
// 리팩토링(2)
arr.sort( (a, b) => a - b );
reverse
arr.reverse
는arr
의 요소를 역순으로 정렬시켜줌- 🚨 배열 자체가 변경됨
let arr = [1, 2, 3, 4, 5];
arr.reverse();
alert( arr ); // 5,4,3,2,1
split
str.split(delim)
을 이용하면 구분자(delimiter)delim
을 기준으로 문자열을 쪼개줌
let names = 'Bilbo, Gandalf, Nazgul';
let arr = names.split(', ');
console.log(arr) // ["Bilbo", "Gandalf", "Nazgul"]
split
메서드는 두 번째 인수로 숫잗를 받아서 배열의 길이를 제한해줄 수 있음
문자열을 글자 단위로 분리하기
split(s)
의s
를 빈 문자열로 지정하면 문자열을 글자 단위로 분리 가능let str = "test"; alert( str.split('') ); // t,e,s,t
join
arr.join(glue)
은split
과 반대 역할을 하는 메서드- 인수
glue
를 접착제처럼 사용해 배열 요소를 모두 합친 후 하나의 문자열을 만들어줌let arr = ['Bilbo', 'Gandalf', 'Nazgul']; let str = arr.join(';'); // 배열 요소 모두를 ;를 사용해 하나의 문자열로 합침 alert( str ); // Bilbo;Gandalf;Nazgul
reduce와 reduceRight
let value = arr.reduce(function(acc, cur, index, array) {
// ...
}, [initial]);
acc
– accumulator, 이전 함수 호출의 결과.initial
은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션)cur
– current, 현재 배열 요소index
– 요소의 위치array
– 배열
- 예시: 누산기
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
- 초깃값이 없으면 reduce는 배열의 첫 번째 요소를 초깃값으로 사용하고 두 번째 요소부터 함수를 호출함
- 🚨 초깃값 없이
reduce
를 사용할 시, 배열이 비어있는 상태면 reduce 호출 시 에러가 발생할 수 있음 arr.reduceRight
는reduce
와 동일한 기능을 하지만 배열의 오른쪽부터 연산을 수행한다는 점이 다름
Array.isArray로 배열 여부 알아내기
- 자바스크립트에서 배열은 독립된 자료형으로 취급되지 않고 객체형에 속하기 때문에
typeof
로는 일반 객체와 배열을 구분할 수가 없음 Array.isArray(value)
는value
가 배열이라면true
를, 배열이 아니라면false
를 반환
alert(typeof {}); // object
alert(typeof []); // object
alert(Array.isArray({})); // false
alert(Array.isArray([])); // true
배열 메서드와 ‘thisArg’
- 함수를 호출하는 대부분의 배열 메서드(
find
,filter
,map
등)는thisArg
라는 매개변수를 옵션으로 받을 수 있음(sort
는 제외) thisArg
는func
의this
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg는 선택적으로 사용할 수 있는 마지막 인수입니다.
- 예시
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30}
];
// army.canJoin 호출 시 참을 반환해주는 user를 찾음
let soldiers = users.filter(army.canJoin, army);
alert(soldiers.length); // 2
alert(soldiers[0].age); // 20
alert(soldiers[1].age); // 23
5.6 iterable 객체
요약
for..of
을 사용할 수 있는 객체를 이터러블이라고 부릅니다.
- 이터러블엔 메서드Symbol.iterator
가 반드시 구현되어 있어야 합니다.
-obj[Symbol.iterator]
의 결과는 이터레이터라고 부릅니다. 이터레이터는 이어지는 반복 과정을 처리합니다.
- 이터레이터엔 객체{done: Boolean, value: any}
을 반환하는 메서드next()
가 반드시 구현되어 있어야 합니다. 여기서done:true
은 반복이 끝났음을 의미하고 그렇지 않은 경우엔value
가 다음 값이 됩니다.- 메서드
Symbol.iterator
는for..of
에 의해 자동으로 호출되는데, 개발자가 명시적으로 호출하는 것도 가능합니다.- 문자열이나 배열 같은 내장 이터러블에도
Symbol.iterator
가 구현되어 있습니다.- 문자열 이터레이터는 서로게이트 쌍을 지원합니다.
인덱스와 length 프로퍼티가 있는 객체는 유사 배열이라 불립니다. 유사 배열 객체엔 다양한 프로퍼티와 메서드가 있을 수 있는데 배열 내장 메서드는 없습니다.
명세서를 보면 대부분의 메서드는 ‘진짜’ 배열이 아닌 이터러블이나 유사 배열을 대상으로 동작한다고 쓰여 있는걸 볼 수 있습니다. 이 방법이 더 추상적이기 때문입니다.
Array.from(obj[, mapFn, thisArg])을 사용하면 이터러블이나 유사 배열인 obj를 진짜 Array로 만들 수 있습니다. 이렇게 하면 obj에도 배열 메서드를 사용할 수 있죠. 선택 인수 mapFn와 thisArg는 각 요소에 함수를 적용할 수 있게 해줍니다.
- 반복 가능한(iterable, 이터러블) 객체는 배열을 일반화한 객체이며, 이터러블 이라는 개념을 사용하면 어떤 객체에든 for..of 반복문을 적용할 수 있음
- 배열과 문자열은 대표적인 이터러블이고 배열 외에도 다수의 내장 객체가 반복 가능함
- 배열이 아닌 객체가 있는데, 이 객체가 어떤 것들의 컬렉션(목록, 집합 등)을 나타내고 있는 경우,
for..of
문법을 적용할 수만 있다면 컬렉션을 순회하는데 유용할 것임
Symbol.iterator
let range = {
from: 1,
to: 5
};
// 아래와 같이 for..of가 동작할 수 있도록 하는 게 목표임
// for(let num of range) ... num=1,2,3,4,5
range
를 이터러블로 만들려면(for..of
가 동작하도록 하려면) 객체에Symbol.iterator
(특수 내장 심볼)라는 메서드를 추가해 아래와 같은 일이 벌어지도록 해야함
for..of
가 시작되자마자for..of
는Symbol.iterator
를 호출합니다.
(Symbol.iterator
가 없으면 에러가 발생합니다)
Symbol.iterator
는 반드시 이터레이터(iterator
, 메서드next
가 있는 객체) 를 반환해야 합니다.- 이후
for..of
는 반환된 객체(이터레이터)만을 대상으로 동작합니다. for..of
에 다음 값이 필요하면,for..of
는 이터레이터의next()
메서드를 호출합니다.next()
의 반환 값은{done: Boolean, value: any}
와 같은 형태이어야 합니다.done=true
는 반복이 종료되었음을 의미합니다.done=false
일땐value
에 다음 값이 저장됩니다.
let range = {
from: 1,
to: 5
};
// 1. for..of 최초 호출 시, Symbol.iterator가 호출됩니다.
range[Symbol.iterator] = function() {
// Symbol.iterator는 이터레이터 객체를 반환합니다.
// 2. 이후 for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해집니다.
return {
current: this.from,
last: this.to,
// 3. for..of 반복문에 의해 반복마다 next()가 호출됩니다.
next() {
// 4. next()는 값을 객체 {done:.., value :...}형태로 반환해야 합니다.
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// 이제 의도한 대로 동작합니다!
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
- 리팩토링: 이터레이터 객체와 반복 대상 객체를 합쳐서 range 자체를 이터레이터로 만듦
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
this.current = this.from;
return this;
},
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
문자열은 이터러블입니다
for..of
는 문자열의 각 글자를 순회함
for (let char of "test") {
// 글자 하나당 한 번 실행됩니다(4회 호출).
alert( char ); // t, e, s, t가 차례대로 출력됨
}
이터레이터를 명시적으로 호출하기
let str = "Hello";
// for..of를 사용한 것과 동일한 작업을 합니다.
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next();
if (result.done) break;
alert(result.value); // 글자가 하나씩 출력됩니다.
}
이터러블과 유사 배열
- 이터러블(iterable) 은 위에서 설명한 바와 같이 메서드 Symbol.iterator가 구현된 객체
- 유사 배열(array-like) 은 인덱스와 length 프로퍼티가 있어서 배열처럼 보이는 객체
- 이터러블과 유사 배열은 대개 배열이 아니기 때문에 push, pop 등의 메서드를 지원하지 않음
Array.from
- 범용 메서드 Array.from는 이터러블이나 유사 배열을 받아 ‘진짜’ Array를 만들어주기 때문에 이 과정을 거치면 이터러블이나 유사 배열에 배열 메서드를 사용할 수 있음
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (메서드가 제대로 동작합니다.)
Array.from(obj[, mapFn, thisArg])
mapFn
을 두 번째 인수로 넘겨주면 새로운 배열에obj
의 요소를 추가하기 전에 각 요소를 대상으로mapFn
을 적용할 수 있음- 세 번째 인수
thisArg
는 각 요소의this
를 지정할 수 있도록 해줌
📚 참고 : javascript.info
Author And Source
이 문제에 관하여(5. 자료구조와 자료형(2)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@protect-me/2저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)