회고록 블로그

[공부 필기] JavaScript 문법 공부 26일차 (1) 본문

2. 프로그래밍 언어 공부/JavaScript

[공부 필기] JavaScript 문법 공부 26일차 (1)

김간장 2022. 5. 27. 16:57

※ 필자는 초초초보자입니다.

※ 틀린 내용에 대한 피드백은 언제든지 환영합니다.

 

 

공부하고 있는 자료 : modern JavaScript tutorial 

https://ko.javascript.info/array-methods

 

배열과 메서드

 

ko.javascript.info

 

✅ 요소 추가.제거 메서드

- 배열의 맨 앞이나 끝에 요소 추가/제거하는 메서드 (앞에서 공부함)

   ► 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

 

배열과 메서드

 

ko.javascript.info

 

► 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 메서드를 이용해서 배열의 각 요소를 붙인다.

   → 붙일 때 구분자는 없다.

Comments