[공부 필기] JavaScript 문법 공부 26일차 (1)
※ 필자는 초초초보자입니다.
※ 틀린 내용에 대한 피드백은 언제든지 환영합니다.
공부하고 있는 자료 : modern JavaScript tutorial
https://ko.javascript.info/array-methods
✅ 요소 추가.제거 메서드
- 배열의 맨 앞이나 끝에 요소 추가/제거하는 메서드 (앞에서 공부함)
► pop : 배열 뒤에서 꺼냄 / push : 배열 뒤에 넣음
► shift : 배열 앞에서 꺼냄 / unshift : 배열 앞에 넣음
- 이 외에도 다른 메서드가 있다.
► splice : 배열에서 요소를 추가/삭제/교체한다.
► slice : 첫번째 인자부터 두번째 인자까지의 요소를 복사한 새로운 배열을 반환한다.
► concat : 기존 배열의 요소를 사용해서 새로운 배열을 만들거나 기존 배열에 요소를 추가한다.
📌 delete와 splice
- delete로 배열의 요소를 지우면 요소는 지워지지만 length는 그대로 남는다.
► 이는 delete가 키를 이용해서 해당 키에 상응하는 값을 지우기 때문이다.
► 값을 삭제한 후 남은 공간은 그대로 남게 된다. (나머지 요소들이 앞으로 당겨오면서 채워지지 않음)
► 이러한 이유로 length가 그대로 남는 것
- 이때 splice 메서드를 사용한다.
► 요소를 추가, 삭제, 교체할 수 있다.
// 삭제
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.splice(1, 1); //인덱스 1부터 시작해서 요소 1개를 제거한다.
console.log(arr); // [1, 3, 4, 5, 6, 7]
// 교체
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.splice(0, 3, "01", "02"); //인덱스 0부터 시작해서 요소 3개를 제거하고 그 자리를 다른 요소로 채운다.
console.log(arr); // ['01', '02', 4, 5, 6, 7]
// 추가
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.splice(2, 0, "03", "04"); //인덱스 2부터 시작해서 요소 0개를 제거하고 그 자리를 다른 요소로 채운다.
console.log(arr); // [1, 2, '03', '04', 3, 4, 5, 6, 7]
📌 splice의 첫번째 인자는 음수(-)가 될 수도 있다.
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.splice(-1, 0, "03", "04");
- 이 코드는 배열의 끝에서 첫 번째 요소(=7)부터 0개의 요소를 삭제하고
"03"과 "04"를 추가하라는 의미라고 한다.
- 이 코드를 실행하면 배열의 요소인 6과 7 사이에 "03"과 "04"가 추가된다.
📌 slice
arr.slice([start], [end])
- "start"인덱스부터 "end"인덱스까지의 요소를 복사한 새로운 배열을 반환한다.
- 이때, "end" 인덱스의 요소는 제외된다. "end" 전까지의 요소이다.
- start와 end에는 음수(-)가 올 수 있다.
이는 배열의 끝에서부터의 요소 개수를 의미한다.
console.log(arr.slice(-3, -1)); // [5,6]으로 구성된 배열
- arr.slice 메서드(배열)와 str.slice 메서드(문자열)의 차이
► 유사하게 동작하는데
► arr.slice는 서브 문자열 대신에 서브 배열을 반환한다.
📌 concat
- 인자로 받은 값이 배열이면 → 배열의 모든 요소가 복사된다.
- 인자로 받은 값이 단순 값이면 → 인자가 그대로 복사된다.
- 인자로 받은 값이 객체이면 → 객체가 분해되지 않고 통으로 복사된다.
- 인자로 받은 값이 유사 배열 객체에 특수한 프로퍼티 Symbol.isConcatSpreadable이면
→ concat이 이 프로퍼티를 만나면 객체를 배열처럼 취급하므로
객체 전체가 아닌 객체 프로퍼티의 값이 더해진다.
✅ forEach로 반복작업 하기
- arr.forEach : 주어진 함수(forEach의 인자인 함수)를 배열 요소 각각에 대해 실행할 수 있게 해준다.
- 문법
arr.forEach(function(item, index, array) {
// 요소에 무언가 작업할 코드
});
- 요소를 모두 alert으로 출력하고 싶을 땐 아래와 같이 사용할 수 있다.
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.forEach(alert);
- 인덱스까지 출력하고 싶다면 아래와 같이 사용할 수 있다.
let arr = [1, 2, 3, 4, 5, 6, 7];
arr.forEach((a, b, c) => {
alert(`item : ${a}, index : ${b}, array : ${c}`);
});
✅ 배열 탐색하기
- indexOf / lastIndexOf / includes
► 문자열 메서드에 있는 indexOf, lastIndexOf, includes와 문법이 동일하가.
► 하는 일도 동일하다.
► 단지 연산 대상이 문자열이 아니라 배열의 요소일 뿐이다.
► 이 메서드들은 요소를 찾을 때 완전 항등 연산자(===)를 사용한다.
- find / findIndex
► find : 특정 조건에 부합하는 객체를 배열 내에서 찾을 수 있다. (반환값 : 해당 요소)
► findIndex : 조건에 맞는 요소를 반환하는 대신 요소의 인덱스를 반환한다. (반환값 : 해당 요소의 인덱스)
let result = arr.find(function(item, index, array) {
// true가 반환되면 반복이 멈추고 해당 요소를 반환합니다.
// 조건에 해당하는 요소가 없으면 undefined를 반환합니다.
});
► 보통 함수의 인자 item 정도를 사용하고, index나 array는 잘 사용하지 않는다고 한다.
► 예시
users.find(item => item.id == 1);
- filter
► find는 조건에 일치하는 요소 하나만을 찾는다면 filter는 여러개를 찾는다.
let results = arr.filter(function(item, index, array) {
// 조건을 충족하는 요소는 results에 순차적으로 더해집니다.
// 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환됩니다.
});
✅ 배열을 변형하는 메서드
- 배열을 변형하거나 요소를 재정렬하는 메소드
► map / sort(fn) / reverse / split과 join / reduce와 reduceRight
📌 map
- 사용 빈도가 높은 메서드 중 하나이다.
- 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 배열로 반환해준다.
- 예시 : 요소의 문자열 길이 찾아서 배열로 만들기
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6
📌 sort(fn)
- 배열의 요소를 정렬해준다. 배열 자체가 변경된다.
- 반환값 : 재정렬된 배열
► 이미 전달된 arr 자체가 수정되었기 대문에 반환 값은 잘 사용되지 않는 편이라고 한다.
- 주의할 점
► 요소는 문자열로 취급되어 재정렬이 된다. (문자형으로 변환된 후 재정렬된다)
► 만약 기본 정렬(문자열 기준 정렬) 대신 새로운 정렬 기준을 만들려면 arr.sort()에 새로운 함수를 넘겨줘야한다.
► 이때 새로운 함수는 반드시 "값 두 개를 비교해야 하고, 반환 값도 있어야 한다"
- 새로운 함수 예시
► 반환값은 어떻게 주어도 상관없는데, 양수를 반환하는 경우 첫번째 인자 > 두번째 인자이고
► 음수를 반환하는 경우 첫번째 인자 < 두번재 인자이다.
"use strict";
let grade = [1, 6, 4, 3, 5, 2];
function compare(a, b) {
if (a > b) return 1;
if (a == b) return 0;
if (a < b) return -1;
}
console.log(grade); //[1, 6, 4, 3, 5, 2]
grade.sort(compare);
console.log(grade); //[1, 2, 3, 4, 5, 6]
► 보통 sort는 사전 편집 순으로 요소를 정렬한다.
(기본적으로 sort는 포괄적인 정렬 알고리즘을 이용해서 구현되어있다고 한다; 대개는 퀵 소트 사용)
► 새로운 함수 compare을 전달받으면,
sort는 주어진 함수를 사용해서 정렬 기준을 만들고 이 기준에 따라 요소들을 재배열한다.
- 새로운 함수 compare을 기준으로 하면 다음과 같이 요소들 비교하며 정렬을 한다.
► 한 요소가 특정 요소와 여러 번 비교되는 일이 생기곤 하는데, 비교 횟수를 최소화 하려다 보면 이렇게 된다고 한다.
function compare(a, b) {
console.log(a + "<>" + b);
return (a - b);
}
grade.sort(compare);
console.log(grade); //[1, 2, 3, 4, 5, 6]
- 문자열엔 locleCompare를 사용하면 된다. (유니코드 기준으로 글자를 비교)
alert( countries.sort( (a, b) => a.localeCompare(b) ) );
📌 reverse
- 배열의 요소를 역순으로 정렬시켜주는 메서드입니다.
- 반환 값 : 재정렬된 배열
📌 split와 join
- Kim, Lee, Park, Choi 와 같은 문자열이 있다고 할 때,
이 문자열을 쪼개서 배열의 요소로 저장하고 싶다면 split을 사용하면 된다.
► 첫번째 인자로 delimiter(구분자)를 전달한다.
► 두번째 인자(선택사항)로 배열의 길이를 제한할 수 있는 숫자를 전달한다.
let str = "Kim, Lee, Park, Choi";
let arr = str.split(', ');
console.log(str); //Kim, Lee, Park, Choi
console.log(arr); //(4) ['Kim', 'Lee', 'Park', 'Choi']
let str = "Kim, Lee, Park, Choi";
// 두번째 인자 : 2
let arr = str.split(', ', 2);
console.log(str); //Kim, Lee, Park, Choi
console.log(arr); //(2) ['Kim', 'Lee']
- 글자 단위로 분리하고 싶다면 빈 문자열을 구분자(delimiter)로 전달하면 된다.
"use strict";
let str = "Kim, Lee,";
let arr = str.split('', 10);
console.log(arr); //(9) ['K', 'i', 'm', ',', ' ', 'L', 'e', 'e', ',']
- 반대로 join 메서드는 배열의 요소를 모두 합쳐준다.
► 첫번째 인자로 각 요소의 사이에 들어갈 구분자?를 전달한다.
"use strict";
let str = ["H", "E", "L", "L", "O", " ", "W", "O", "R", "L", "D"];
console.log(str.join(" ")); //H E L L O W O R L D
console.log(str.join("")); //HELLO WORLD
📌 reduce와 reduceRight
- forEach, for, for...of를 사용해서 배열의 요소에 각각 접근을 해서 원하는 작업을 한 후
map을 사용해서 작업 결과물을 새로운 배열 형태로 만들 수 있다.
- rudece와 reduceRight도 이런 메서드와 유사한 작업을 한다.
► 배열을 기반으로 값 하나를 도출할 때 사용
- 문법
► 인자는 두 개이다. 함수와 initial(선택사항)
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
► 첫번째 인자인 함수에는 네 개의 매개변수가 있다.
# accumulator : 이전 함수 호출의 결과 (initial은 함수 최초 호출 시 사용하는 초깃값을 나타낸다)
# item : 현재 배열 요소
# index : 요소의 위치
# array : 배열
► 이전 함수 호출의 결과인 accumulator는 함수를 호출할 때 첫번째 인수로 사용된다.
(이전의 결과들을 누적하여 저장하는 누산기라고 생각하면 된다)
마지막 함수까지 호출되면 이 값은 reduce의 반환 값이 된다고 한다.
※ 너무 어려우니 예시와 동작방법을 생각해보자..
reduce는 두 개의 인자를 받고 있다. 하나는 화살표 함수이고, 또다른 하나는 0이다.
let str = ["Hello", "World", "!"];
let result = str.reduce((previousValue, currentValue) => currentValue, 0);
console.log(result); // !
- 여기서 0은 reduce의 첫번째 인자인 [함수]를 최초로 호출할 때, 함수의 첫번째 인자(accumulator)를 초기화하는데 사용한다.
► 즉, reduce의 첫번째 인자인 [화살표 함수]를 최초로 호출할 때,
해당 함수의 첫번째 인자인 previousValue를 0으로 초기화한다.
- 함수의 두번째 인자는 currentValue이다. 현재의 배열 요소를 가리키는 매개변수(위에서 언급했던 item)이다.
- 함수의 세번째 인자와 네번째 인자는 생략됐다.
- 동작은 다음과 같다.
1) 함수를 최초로 호출할 때 previousValue를 0으로 초기화한다. (previousValue = 0;)
2) currentValue에는 배열의 첫번째 요소(="Hello")가 할당된다.
3) 두 개의 인자를 가지고 currentValue를 반환한다. 즉, 배열의 첫번째 요소(="Hello")가 반환된다.
4) 함수를 두번째로 호출할 때 previousValue는 이전의 결과(="Hello")가 된다.
5) currentValue는 배열의 두번째 요소(="World")가 된다.
6) currentValue, 즉 배열의 두번째 요소(="World")가 반환된다.
7) 함수를 세번째로 호출할 때 previousValue는 이전의 결과(="World")가 된다.
8) currentValue는 배열의 세번째 요소(="!")가 된다.
9) 반환 되는 값은 배열의 세번째 요소(="!")가 된다.
10) 마지막까지 호출되었으니 return 되는 값은 "!"가 된다.
11) 결과적으로 변수 result는 "!"가 저장된다.
- reduce는 다음과 같이 사용될 수 있다.
► 배열의 요소가 문자열일 때, 문자열의 길이를 모두 더하고 싶을 때
함수를 최초로 호출했을 땐 previousValue는 0이고
두번째로 호출했을 땐 previousValue는 이전의 결과인 첫번째 요소의 길이(length)가 저장된 상태이며
세번째로 호출했을 땐 previousValue는 첫번째 요소의 길이(length) + 두번째 요소의 길이(length)가 된다.
let str = ["Hello", "World", "!"];
let result = str.reduce((previousValue, currentValue) => previousValue + currentValue.length, 0);
console.log(result); // 11
- reduce의 두번째 인자인 initial를 생략하면 배열의 첫번째 요소를 초기값으로 사용하고
두번째 요소부터 함수를 호출한다.
- 주의해야할 점
► 배열이 비어있는 상태면 reduce 호출 시 에러가 발생한다.
► 그렇기 때문에 항상 초기값(initial)을 명시해줄 것을 권장한다.
- arr.reduceRight는 배열의 오른쪽부터 연산을 수행한다.
✅ Array.isArray로 배열 여부 알아내기
- typeof로는 배열과 객체를 구분할 수 없다.
► 배열도 객체형에 속하기 때문
- 배열인지 아닌지 감별해내는 특별한 메서드가 있다.
► Array.isArray(value);
✅ 배열 메서드와 'thisArg'
- 함수를 호출하는 대부분의 배열 메서드(find, filter 등)는 thisArg라는 매개변수를 옵션으로 받을 수 있다.
► sort는 제외
- thisArg는 this 키워드와 관련이 깊다.
- find, filter, map에서 thisArg를 사용하면 함수의 this가 된다.
► thisArg는 두번째 인자가 아니고 마지막 인자이다.
arr.find(func, thisArg) // func의 this가 된다.
- 예시
► 객체의 메서드를 map의 첫번째 인자로 주고, thisArg는 객체 obj를 주었다.
► map 메서드는 각 요소에 하나씩 접근해서 그 반환값으로 새로운 배열을 만든다.
► thisArg는 obj의 컨텍스트 정보를 넘겨준다.
let obj = {
count: 0,
showMessage(user) {
for (let value of user) {
console.log(value);
this.count++;
}
console.log(this.count);
return (user);
},
}
let user = ["kim", "lee", "park"];
let person = user.map(obj.showMessage, obj);
- 만약 여기에서 thisArg가 없다면 obj.showMessage는 단독 함수처럼 취급이 되고, 함수 본문 내 this는 undefined가 되어 에러가 발생한다.
"use strict";
let obj = {
count: 0,
showMessage(user) {
for (let value of user) {
console.log(value);
this.count++;
}
console.log(this.count);
return (user);
},
}
let user = ["kim", "lee", "park"];
let person = user.map(obj.showMessage); //TypeError: Cannot read properties of undefined (reading 'count')
- thisArg를 사용하지 않고 할 수 있는 방법도 있다.
let person = user.map(user => obj.showMessage(user));
- 그런데 thisArg를 더 많이 사용한다고 한다.
✅ 과제
https://ko.javascript.info/array-methods#ref-520
► border-left-width를 borderLeftWidth로 변경하기
- 내가 쓴 코드 (ㅠㅠ)
function camelize(dashStr) {
let arr = dashStr.split("");
do {
let resIdx = arr.findIndex((item, index) => item === '-');
if (resIdx === -1)
return (arr.join(""));
arr.splice(resIdx, 2, arr[resIdx + 1].toUpperCase());
} while (1);
}
- 정답
function camelize(str) {
return str.split('-').map((word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)).join('');
}
str.split('-')
→ str을 '-' 기준으로 자른다.
→ 만약 str = "background-color" 이라면 "-"를 기준으로 배열이 만들어지며 각 요소는 다음과 같다.
→ ["background", "color"]
→ 첫번째 요소는 "background", 두번째 요소는 "color"
str.split('-').map((word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1))
→ map 메서드를 이용해서 새로운 배열을 만든다.
→ 이때, word의 인덱스가 0이면? 기존의 word를 그대로 둔다.
→ 만약 word의 인덱스가 0이 아니면? word의 첫번째 문자를 대문자로 변환하고 word의 두번째 문자부터 끝까지 다 복사해서 붙인다.
→ 이렇게 새로운 배열을 만든다.
→ 여기에서 word는 배열을 기준으로 첫번째 요소(="background")와 두번째 요소(="color")를 의미하며
→ word[0]은 각 문자열의 첫번째 문자("b" 와 "c")를 의미한다. (아래 코드 참고)
let str = "hello";
console.log(str[0]); //h
str.split('-').map((word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)).join('')
→ join 메서드를 이용해서 배열의 각 요소를 붙인다.
→ 붙일 때 구분자는 없다.