JS method사용법, iterator, 에러 핸들링(2024-11-13수업)

짝은별·2024년 11월 13일

JS

목록 보기
11/23
post-thumbnail

숫자형 method

삼각함수

숫자형의 계산에 존재하는 sin과 cos을 이용하여 원하는 좌표로 이동이 가능하다

function toRadian(degree) {
  return degree * (Math.PI / 180);
}

let deg = toRadian(45);
let radius = 10;

x += radius*Math.cos(deg);
y += radius.Math.sin(deg);

와 같은 방식을 이용하여 각도를 조절하거나 반지름을 조절하여 (x,y)를 중심으로 반지름이 radius인 원 위를 지나는 점을 찍을 수 있다

이를 연속적으로 실행시킨다면(setInterval활용) 애니메이션을 구현할 수 있다

let lastInsertTime = 0;

function animate() {
  degree += 0.3;
  radius += 0.1;
  const x = Math.cos(toRadian(degree)) * radius;
  const y = Math.sin(toRadian(degree)) * radius;

  circle.style.transform = `translate(${x}px,${-y}px)`;

  const currentTime = Date.now();

  if (currentTime - lastInsertTime >= 300) {
    const tag = `<div style="transform:translate(${x}px,${-y}px)">★</div>`;

    document.querySelector('.space').insertAdjacentHTML('beforeend', tag);
    lastInsertTime = currentTime;
  }
}

setInterval(animate, 5);

하지만 위와 같은 코드를 작성하게 되면 div태그가 많이 생성된다
따라서 이는 과도한 부하가 걸릴 수 있다

따라서 canvas태그에 따로 정리하는 것도 방법이다

문자열 method

문자열의 작성

다시 한 번 언급하고 지나가겠다
문자열은 총 3가지의 literal 방법이 존재한다

  • ' '
  • " "

  • 마지막은 template literal이라고 불리고 특이한 점은 중간에 ${}를 이용해 변수를 삽입할 수 있다는 점이 있다

그리고 문자열을 선언할 때 특수기호 앞역슬래시(\)를 붙여 escape처리를 해줄 수 있다

여러가지 method

  • length
    문자열의 길이를 구할 수 있는 method입니다
    length를 알 수 있다는건 for...of문을 수행할 수도 있다는 뜻으로 이어집니다

그렇기에 이는 배열처럼 접근이 가능하다
또한 문자열immutable하다

const str = 'hello';
str[2] = 'k'; // str[2]는 첫번째 l을 가리킨다
console.log(str) // hello

만약 특정 index에 존재하는 문자다른 문자로 바꿔도
기존의 문자는 유지가 된다

  • toLowerCase() , toUpperCase()
    이는 모든 문자열소문자로, 혹은 대문자변환이 가능

  • slice
    slice를 사용하여 문자열에서 원하는 부분을 잘라낼 수 있다

const str = 'abcdefg';

const slice = str.slice(2, 5);

console.log(slice); //cde

이는 2번째 index부터 5번째 index전까지를 반환한다

만약 slice(a,b)에서 b에 -1을 부여하면 a번째 index부터 문자열의 끝까지 반환한다
또한 b를 지정하지 않게 되면 b에 -1을 지정한 것처럼 작동한다
그리고 a,b둘 다 지정하지 않으면 문자열을 그대로 반환한다

substring

slice와 유사한 부분이 있다
substring(a,b)에서 a와 b에 index를 부여하면 slice와 같은 동작을 수행한다

이때 b에 -1을 부여하면 처음부터 a번째 index전까지 반환한다

subStr은 현재 사용되지 않는다

indexOf

indexOf를 사용하여 해당 문자열몇 번째에 존재하는지 확인할 수 있다
단, 찾으려는 문자가 존재하지 않는다면 -1을 반환한다

const str = 'hello';
console.log(str.indexOf('e')); // 1

lastIndexOf

indexOf와 비슷한 동작을 수행하지만
만약 찾으려는 문자가 여러개라면 가장 마지막에 등장한 index를 반환한다

includes

includes를 사용하여 찾으려는 문자가 문자열에 포함되어있는지 확인할 수 있다

const str = 'hello';
console.log(str.includes('l')); // true

startsWith

해당 문자열이 예상한 값으로 시작하는 체크할 수 있다

const str = 'hello'
console.log(str.startsWith('el')); // false
console.log(str.startsWith('he')); // true

endsWith

startsWith와 동일하게 작동하지만 마지막을 체크한다

trim , trimRight, trimLeft

만약 문자열을 ' a b c d ' 라고 해보자
이때 왼쪽과 오른쪽 공백제거하고 싶다면 trim을 사용해야한다

const str = '   a    b  c   d   ';
const str1 = str.trimRight(); // '   a    b  c   d'
const str2 = str.trimLeft(); // 'a    b  c   d   '
const str3 = str.trim(); // 'a    b  c   d'

replaceAll

만약 위의 경우에서 문자와 문자 사이에 있는 공백까지 제거할때 주로 사용한다

const str = '   a    b  c   d   ';
const str1 = str.replaceAll(' ' ,''); // abcd

이는 정규 표현식을 이용해 replace로도 가능하다

repeat

repeat을 이용해서 문자열을 반복시켜줄 수 있다

const str = 'abc';
const str1 = str.repeat(5);
console.log(str1); // abcabcabcabcabc

배열 method

우선 배열의 요소들 즉, element들의 문자 type은 제약이 없다는 것을 기억하자

pop,push / shift,unshift

스택과 큐에 주로 사용된다
Stack(스택)LIFO(Last In First Out), Queue(큐)FIFO(First In First Out)으로 동작한다
또한 이를 모두 갖춘 것을 Deque(데큐)라고 부른다

pop과 push배열의 마지막에, shift와 unshift배열의 처음에 영향을 끼친다
pop배열의 마지막의 있는 값을 제거하고 반환, shift배열의 처음에 있는 값을 제거하고 반환한다
push배열의 마지막에 요소를 추가하고 배열의 길이를 반환, unshift배열의 처음에 요소를 추가하고 배열의 길이를 반환한다

따라서 JS에서 배열Deque구조라고 봐도 무방하다

참고로 shift와 unshift시간복잡도가 O(N)이므로 주의해야한다

배열과 객체

배열도 객체이다 하지만 이때 배열을 객체처럼 사용한다면
배열을 계산할때 사용되는 최적화 기법을 제대로 수행하지 못할 가능성이 있다
따라서 순서가 있는 문자열인 배열의 이점이 사라진다

const arr = [1,2,3];
arr.name = 'amy';

length

배열의 length배열 내부에 존재하는 요소들의 갯수가 아닌 배열의 마지막 index+1을 반환한다
이 얘기는 배열[1, undefined, 2, undefined,3]일때 요소는 분명 3개이지만 length는 5를 반환한다는 의미이다

const arr = [1, undefined, 2, undefined,3];
console.log(arr.length); // 5

배열의 길이도 역시 조절할 수 있다

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

이때 기존의 배열보다 길이를 짧게 지정하면 초과된 요소들은 삭제된다
이를 활용하여 배열의 길이를 0으로 만든다면 배열을 초기화할 수 있다

생성자 함수 생성

생성자 함수 생성 방식으로도 배열을 생성할 수 있다
이때 길이를 지정하기도 한다

const arr = new Array(5);
console.log(arr); // [empty × 5]

2차원 배열

배열은 기존의 알고 있는 방식은 1차원이다
하지만 배열의 요소에 배열을 넣게 되면 2차원이 된다
이는 x와 y가 존재하기 때문이다

const arr = [
  [1,2],[3,4],[5,6]
 ];

배열 타입 체크

배열typeof로 확인하는 순간 'object'라고 출력된다
따라서 이를 해결하기 위해선 Array.isArray()를 활용한다

const arr = [1,2,3,4,5];
console.log(typeof arr); // object
console.log(Array.isArray(arr)); // true

method에 관한 새로운 관점

기존의 forEach와 같은 배열의 method들은 함수를 직접 작성하여 실행했었다
하지만 함수가 들어가야 하는 자리에 외부 함수를 할당해도 정상적으로 작동한다

const arr = [1,2,3,4,5];

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

arr.forEach(print);

이벤트 순환

web api의 시점으로 이벤트를 부여하는 것은 생각보다 큰 과부하를 줄 수 있다
따라서 위의 언급했던 forEach와 같은 method들배열을 순환하며 이벤트를 할당한다면
버거워할수도 있다

이때 delegation(위임)을 사용하여 이벤트를 추가하는 방식이 효율적이다

reverse

배열의 순서를 반대로 뒤집을 때 사용한다
하지만 이는 배열의 원문을 훼손하여 위험하다
이를 방지하고자 toReversed()라는 method가 존재한다

const arr = [1,2,3,4,5];
const arr1 = arr.reverse();
console.log(arr); // [5, 4, 3, 2, 1] arr 훼손
---
const arr = [1,2,3,4,5];
const arr1 = arr.toReversed();
console.log(arr); // [1, 2, 3, 4, 5] arr 훼손X

splice

문자열에서 봤던 splice와 같은 행동을 한다
하지만 차이점splice는 배열의 원문을 훼손한다는 점이다
따라서 이를 해소하고자 toSpliced라는 method가 등장했다

const arr = [1,2,3,4,5];
const arr1 = arr.splice(2,4);
console.log(arr); // [1, 2] arr 훼손
---
const arr = [1,2,3,4,5];
const arr1 = arr.toSpliced(2,4);
console.log(arr); // [1, 2, 3, 4, 5] arr 훼손X

참고로 splice(2,4)2번째 index부터 4개의 element삭제 후 반환하겠다는 의미이다

sort

sort 내부compare function(callback function)을 이용하여 배열을 정렬할 수 있다
하지만 이도 역시 원문을 훼손하여 위험하다
이를 해소하고자 toSorted라는 method가 존재한다

const arr = [1,4,5,2,3];
const arr1 = arr.sort((a,b) => a-b); //오름차순으로 정렬
console.log(arr); // [1, 2, 3, 4, 5] arr 훼손
---
const arr = [1,4,5,2,3];
const arr1 = arr.toSorted((a,b)=>a-b);
console.log(arr); // [1, 4, 5, 2, 3] arr 훼손X

find

배열에서 원하는 element추출할때 유용하게 사용된다
하지만 하나의 원소만 추출한다
그리고 item 그 자체를 추출한다
(객체라면 객체로, 문자라면 문자로)

const arr = [1,2,3,4,5];
const e = arr.find((item) => return item===5)
console.log(e); // 5

filter

배열에서 조건에 부합하는 모든 element를 추출할때 사용한다
이때 return배열 형식으로 출력된다

const arr = [1,2,3,4,5];
const arr1 = arr.filter((item) => item%2 === 1)
console.log(arr1); // [1, 3, 5]

iterator

기본적으로 for...of를 사용할 수 있다면 iterable하다고 생각한다
iterator고효율 반복을 위해 사용한다
그렇기에 단순 반복을 계속해서 하는 것이 아닌 순환이 한 번 종료되었다면 iterator도 종료된다

const arr = [1, 2, 3, 4, 5];
const iter = arr[Symbol.iterator]();

for (let i = 0; i < arr.length; i++) {
  console.log(iter.next().value);
}

console.log(iter.next().value);

이때 for문을 돌며 1~5까지 출력한다
하지만 다음에 iter의 값을 출력하려고 했더니 undefined를 출력한다
이것이 iterator의 작동원리이다

그렇다면 iterator가 종료되었는지 어떻게 확인을 할까?
바로 iterator 객체를 살펴보면 된다
iterator 객체index와 done의 여부를 포함하고 있다
순환이 완료되었다면 donetrue에서 false로 변환된다

(순환이 완료된 iterator이다)

또한 iterator핵심관심사의 분리에 있다
iteratorindex를 저장한 iterator 객체iterator가 순회할 반복 대상분리하고 있다
하지만 이건 단점도 유발한다

iterator단점은 존재한다
바로 두 개 이상의 for...of문을 사용할 수 없다는 점이다
이유는 관심사의 분리에 있다
iteratoriterator 객체순회할 대상분리했다고 했다
따라서 iterator 객체실행되는건 공유가 된다는 의미이기 때문이다

무한개의 iterator

우선 이것을 살펴보기 전에 generator function에 대해 먼저 살펴보자

  • generator function이란?
    사용법 : function뒤에 *을 붙여 나타낸다(function* idGenerator() {})
    이러한 방식으로 생성된 함수는 iterator를 내장한다
    또한 return을 사용하여 값을 내보내는 것이 아닌 yield를 사용하여 내보낸다
    이때 returnyield동일한 동작을 하는 것은 아니다
    yield는 만나게 되면 우선 값을 반환하고 함수의 동작을 일시 정지한다
    그리고 다시 호출되면 iterator가 동작하여 다음 yield값을 찾는다
    따라서 무한루프를 만나더라도 함수에 갇혀 무한루프가 평생 도는 것이 아닌 yield를 우선 반환한다
function* count(n) {
	for(let i = 0 ; i<n ; i++ {
		yield i;
	}
}

const cnt = count(5);

console.log(cnt.next().value); // 0
console.log(cnt.next().value); // 1
console.log(cnt.next().value); // 2
console.log(cnt.next().value); // 3
console.log(cnt.next().value); // 4

그렇다면 무한개의 iterator가 생성되는 예제를 살펴보자

function* idGenerator() {
  while (true) {
    yield `user-${crypto.randomUUID()}`;
  }
}

const id = idGenerator();

이는 randomUserId를 출력하는 함수이다
하지만 generator function으로 선언했다
따라서 idGenerator()호출할때 한 번의 값만 반환한다

하지만 언제 호출해도 항상 동작한다 이유는 while문으로 인한 무한 iterator를 생성하기 때문이다

iterator의 표현

iterator명시적으로 표현해줄 수 있다

const arr = [1,2,3,4,5];
let iter = arr.[Symbol.iterator]();

문자열iterable하다고 얘기했었다
이는 유사배열로 동작한다
그렇다면 유사배열이라면 iterable일까?
절대 그렇지 않다 물론 유사배열일때 iterable일 수 있지만 아닌 경우가 존재한다는 의미이다

그렇다면 의미적으로 iterable하다면 배열이거나 유사배열이라는 의미일까?
이것도 역시 그렇지 않다

let arrayLike = { // 인덱스와 length프로퍼티가 있음 => 유사 배열
  0: "Hello",
  1: "World",
  length: 2
};

// Symbol.iterator가 없으므로 에러 발생
for (let item of arrayLike) {}

위의 코드는 유사배열이지만 iterable하지 않은 예시이다

만약 이를 해결하고 싶다면 진짜 배열로 변환하는 것이 방법이다

Array.from(arrayLike);

try...catch

만약 스크립트동기적으로 작동 시 에러가 발생한다면 그 뒤에 적힌 코드들실행되지 못하고 바로 스크립트가 '죽는다'

이후 바로 error를 출력한다

이때 스크립트죽는 것을 방지하고자 try...catch를 사용한다
주의해야 할 점은 이는 런타임 시에만 동작하고 실행되지 못하는 코드에는 동작하지 않는다는 점이다

try {
  {{{{{{{{{{{{
} catch(e) {
  alert("유효하지 않은 코드이기 때문에, 자바스크립트 엔진은 이 코드를 이해할 수 없습니다.");
}

생각해보면 수많은 { 로 인해 오류가 발생한다 따라서 오류try...catch로 전송하면 되는거 아닌가? 라고 생각하지만
try는 실행될 수 없는 구문이다 따라서 try...catch가 정상적으로 작동하지 않는다

또한 동기적으로 작동한다

예를 들어 setTimeout을 생각해보자
이는 비동기작동하기 때문에 try...catch가 잡아낼 수 없다
만약 그럼에도 불구하고 이 비동기의 오류try...catch로 잡아내고 싶다면 어떻게 해야할까?
답은 간단하다 바로 비동기로 선언된 곳 내부try...catch를 배치하면 된다

try...catch의 사용처

그렇다면 스크립트가 죽는 것을 방지하는 것이라고 이해는 했다
하지만 정작 어디에 사용되는지 감은 오지 않았다

이는 서버와 통신하는 과정에 많이 사용된다
이는 비동기로 통신하게 되고 따라서 try...catch가 상당히 유용하다

하나의 예시를 들어보자
만약에 data를 가져오는 시간이 1초가 걸린다고 가정해보자
하지만 사용자 입장에서 이 과정에서 오류가 난다고 했을때 만약 고지해주지 않는다면 절대 알아차릴 수 없다
따라서 try...catch로 에러를 던져주는 것이다

그렇다면 서버 통신에서 자주 일어나는 오류에 대해 한 번 살펴보자
JSON을 통해 서버와 통신하게 된다
이때 넘겨주는 data는 문자열로, 받아오는 data는 객체로 받아온다
따라서 변환하는 방법JSON.stringify()(문자열로 변환)JSON.parse()(객체로 변환)를 사용한다

통신 과정 중변환 과정 혹은 JSON파일을 검사할때 잘못된 JSON형식일 경우 에러를 발생시켜 스크립트가 죽는다

따라서 이러한 오류판별하고자 try...catch와 함께 throw를 사용하는 것이다
throw는 에러를 발생시킨다

이때 error는 객체name과 stack 그리고 message와 같은 key를 포함한다

rethrow

만약 하나의 동작에서 여러 군데에 걸쳐 error가 발생할 수 있다
이때 하나의 try...catch만 존재하게 된다면 어떤 곳에서 오류가 발생하든 하나의 오류라고만 판단한다

따라서 이를 방지하고자 rethrow를 활용한다

이는 간단하다
ifthrow를 활용하여 error에 대한 해석을 하고 만약 조건에 부합하다면 적당한 위치에 throwtry...catch를 하는 것이다

try...catch 예제

try {

  alert('try 블록 시작');  // (1) 

  lalala; // 에러, 변수가 정의되지 않음!

  alert('try 블록 끝(절대 도달하지 않음)');  // (2)

} catch(err) {

  alert(`에러가 발생했습니다!`); // (3) 

}

이때 try를 실행하고 1번은 실행된다
하지만 2번에 선언되지 않은 변수가 사용되어 error를 유발한다
원래는 스크립트가 죽어야하지만 try...catch를 이용해 catch가 실행된다

try {
  lalala; // 에러, 변수가 정의되지 않음!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at ... (호출 스택)

  // 에러 전체를 보여줄 수도 있습니다.
  // 이때, 에러 객체는 "name: message" 형태의 문자열로 변환됩니다.
  alert(err); // ReferenceError: lalala is not defined
}

각각 친절하게 분리되어 표시된다

profile
FE(철 아님) 개발자 꿈꾸다

0개의 댓글