삼각함수
숫자형의 계산에 존재하는 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태그에 따로 정리하는 것도 방법이다
문자열의 작성
다시 한 번 언급하고 지나가겠다
문자열은 총 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
우선 배열의 요소들 즉, 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]
기본적으로 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의 여부를 포함하고 있다
순환이 완료되었다면 done은 true에서 false로 변환된다

(순환이 완료된 iterator이다)
또한 iterator의 핵심은 관심사의 분리에 있다
iterator는 index를 저장한 iterator 객체와 iterator가 순회할 반복 대상을 분리하고 있다
하지만 이건 단점도 유발한다
iterator도 단점은 존재한다
바로 두 개 이상의 for...of문을 사용할 수 없다는 점이다
이유는 관심사의 분리에 있다
iterator는 iterator 객체와 순회할 대상을 분리했다고 했다
따라서 iterator 객체가 실행되는건 공유가 된다는 의미이기 때문이다
무한개의 iterator
우선 이것을 살펴보기 전에 generator function에 대해 먼저 살펴보자
generator function이란?function뒤에 *을 붙여 나타낸다(function* idGenerator() {})iterator를 내장한다return을 사용하여 값을 내보내는 것이 아닌 yield를 사용하여 내보낸다return과 yield는 동일한 동작을 하는 것은 아니다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);
만약 스크립트가 동기적으로 작동 시 에러가 발생한다면 그 뒤에 적힌 코드들은 실행되지 못하고 바로 스크립트가 '죽는다'
이후 바로 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를 활용한다
이는 간단하다
if와 throw를 활용하여 error에 대한 해석을 하고 만약 조건에 부합하다면 적당한 위치에 throw후 try...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
}
각각 친절하게 분리되어 표시된다