☑️ flow control, 제어 흐름, 흐름 제어
사용자는 컴퓨터에게 명령하고, 컴퓨터는 이를 수행한다.
특정 이벤트(trigger)로 시작해서~ 원하는 결과를 도출하며 종료된다.
명령형 프로그램의 개별 명령문, 명령 또는 함수 호출이 실행되거나 평가되는 순서
- goto
- 다른 구문에서 시작.
- 개발 설계의 오류를 발생시킬 수 있어 사용하지 말것!
- choice
- 일부 조건이 충족되는 경우에만 일련의 명령문 실행
- `if-else`, `switch`
- loop
- 어떤 조건이 충족될때까지 일련의 명령문을 0회 이상 실행
- Collection loop, General loop > 자료구조에 따라 종류가 존재한다.
- continue
- 현재 실행 구문에서 떨어진 한 구문의 집합을 실행
- Loop continuation
- break
- 프로그램 실행을 중단
- Loop early exit(루프 중지), 함수 실행 정지
표현식 expression
어떤 값으로 이행되는 임의의 유효한 코드 단위
표현식이 평가되면, 새로운 값을 생성하거나 기존 값을 참조한다.
값으로 평가될 수 있는 문은 모두 표현식이다.
ex) 리터럴 표현식,, 함수 표현식...
☑️ 문 statement
프로그램을 구성하는 기본 단위, 최소 실행 단위 `== 명령문`
ex) 선언문, 할당문, 제어문, 반복문, 블럭문 ...
블럭문 block statement : 명령문들을 그룹을 한쌍의 중괄호로 묶어서 표현한다.
제어문 control flow statement
- choice 조건문 : `if-else` `switch` 일부 조건이 충족되는 경우에만 일련의 명령문을 실행한다.
- loop - loop early exit : `break`
- loop - loop continuation : `continue`
- subroutine exit : `return`
- non local control flow : `try-catch` `throw` `generator` `async`
- Conditional loop : `while` `do-while`
- general loop : `for`
- collection loop : `for-in` `for-of`
조건문 if-else, switch
if-else
논리 조건의 참/거짓에 따라 명령문을 실행해야 할 경우 사용된다.
if 문의 논리 조건에는 true, false 로 평가할 수 있는 표현식을 대입 가능하다.
참일경우 > if 블럭문
거짓일경우 > else 블럭문
else if절
falsy (false, undefined, null, 0, NaN, "")는 false 로 평가된다.
중첩 if문 > 지향
switch 문
switch에 명시된 표현식을 평가한 후, 평가된 값과 case 라벨 값을 비교하여 일치하는 case 의 명령문을 실행한다.
- default : 평가된 값에 해당하는 case 문이 없을경우 default의 명령문이 실행된다.
라벨 별로 return break 등 case를 종료시키는 코드가 없으면 무한 실행된다.
Loop 반복분 Conditional loop, For loop, break, continue
조건부 loop
- 루프가 시작할때 조건을 평가 : `while`
- 시작할때 통과가 안하면, 본문 생략 가능
- `while(표현식){ 본문 };`
- 마지막에 조건을 평가 : `do-while`
- 본문은 항상 한번 이상 실행
- `do{ 본문 } while(표현식);`
종료될 수 있는 조건! 을 만들어놓지 않으면 무한 루프에 빠질수 있으니 주의
For loop
특정 부분의 코드가 반복적으로 수행될 수있도록 한다
- 초기화 - 조건식 - 증감문
- C언어에서 유래
- loop 변수의 비교 및 증감으로 count 관리를 해야 한다
- `for(초기문; 조건문; 증감문;){....}`
- 초기문 : loop 변수 초기화
- 조건문 : 코드 실행 전, 조건을 평기해 loop 지속할지 판단
- 증감문 : 코드 실행 후 실행되는 문장 > 루프변수 증감 용도, 컨트롤 하는 부분
- 집합 Collection loop 형식 - ForEach loop
- 컬렉션 안의 항목들을 횡단하는 제어흐름문
- 카운터 루프변수를 관리하지 않는다
- `for ... of`
- 반복 가능한 객체를 통해 반복하는 루프를 만든다
- `for(변수 fo 객체) { ... }`
- Array, Set, String, ...
- `for ... in`
- 객체의 열거속성을 통해 지정된 변수를 반복한다
- `for(변수 in object객체 ) { ... }`
- 객체의 속성값 key 을 반환할 수 있다
break, continue 문
break : 제어 흐름 종류증 프로그램 실행 중단의 종류
반복문, switch문 종료
가장 가까운 반복문을 종료하고, 다음 명령어로 넘어간다.
continue : 현재 실행 구문에서 떨어진 한 구문의 집합을 실행하는 제어흐름 종류에 속한다.
while, do,..while, for문을 다시 시작하기 위해 사용한다. 가장 안쪽의 반복을 종료하고, 다음 반복을 실행한다.
while > 조건문으로 이동
for > 증감문으로 이동
☑️ 예외처리 Exception
의도치 않은 상황을 제어해야 한다. handeling, control
예외상황을 핸들링하지 않는다면, 기능이 동작하지 않거나 어플리케이션이 shut down 될 수 있다.
원인 : 언어레벨 ~ 외부요인 등
- 코드 레벨에서의 문제
- 디바이스의 문제
- 라이브러리 손상
- 사용자의 입력 실수
ECMAScript Error , DOMException
- ECMAScript Error : 자바스크립트 언어에서 발행하는 에러 타입
- RangeError : 값이 집합에 없거나 허용 범위가 아닐때
- ReferenceError : 존재하지 않는 변수 참조 시
- SyntaxError : 문법을 지키지 않았을 때
- TypeError : 값이 기대한 자료형이 아니여서 연산이 불가능할때
- ....
- DOMException : web API 레벨에서 발생하는 에러 타입
- NetworkError : 네트워크 에러 발생 시
- AboutError : 작업이 중단되었을 때
- TimeoutError : 작업 시간이 초과되었을 때
- 그외 에러들
- 개발자도 예상못한 상황
- 개발자가 에러를 직접 상황을 예상해서 핸들링해야한다
- Error 객체를 사용해 에러를 정의내리고 핸들링할 수 있다.
throw, Error 객체로 에러 핸들링
throw문 : 예외를 발생시킬때 사용한다.
catch 블럭에서 에러 객체를 핸들링한다
예외가 발생하면?
- 현재 함수의 실행이 중지
- 에러객체와 함께 에러가 throw
- 제어 흐름은
- 호출자 사이에 catch 블럭이 있으면, catch 블럭으로 전달
- catch 블럭이 없으면, 프로그램 종료
`throw 표현식;`
이후의 명령문은 실행되지 않는다.
Error 객체 : 사용자가 정의힐 수 있다.
`new Error('에러 메시지);`
`Error.message = ''`
`Error.name = ''`
ECMAScript 표준 내장 오류 유형이 있다.
try-catch 문
에러 catch : 에러를 catch해서 프로그램이 종료되지 않도록 해야한다.
예외처리하는 catch 핸들러를 콜스택에서 찾아 올라가야한다
*콜 스택 : 자바스크립트 코드가 실행되면서 실행 컨텍스트가 생성되고, 함수 호출할때마가 스택이 쌓이고, 함수의 실행이 종료되면, 콜스택에서 스택을 제거하는 원리이다
즉 에러 throw 되면, 콜스택을 확인하여, 핸들링 하고 있는 catch문이 있는 스택에서 처리한다.
try-catch 문 : 블록 내에서 예외가 발생할 경우 예외 처리를 맡을 하나 이상의 반응 명령문을 지정한다
- try 블록의 명령문 중 하나가 실패하면 catch로 제어권이 넘어간다
- try 블록의 명령문 중 하나가 성겅하면 catch로 제어권이 넘어가지 않는다
인자로 throw 된 catchId를 참조할 수 있다.
`try { ... } catch(catchId) { ... }`
예외가 처리되면, 더이상 예외가 타고 올라오지 않는다.
finally 블록 : 예외상황이 발생하지 않더라도 무조건 실행된다
`try { ... } catch(catchId) { ... } finally { ... }`
모든상황에서 쓰는건 X
개발자가 예측하기 어려운 에러에 사용 O
외부에 의존되어있는 로직에 사용 O
☑️ 객체, 빌트인 객체
객체 Object
속성을 가진 독립적인 개체 entity > 속성으로 구성된 집합
JS == 객체 기반 프로그래밍 언어
const 객체명 = {
"속성" : 값,
"속성2" : 값
}
속성 property : key-value 로 구성되어 있는 연결 관계. 변수가 아닌 객체에 속해있다.
`objectName.propertyName` 마침표 표기법
`objectName["propertyName"]` 대괄호 표기법
값 뿐만 아니라 함수도 할당 가능하다 > 객체에 속해있는 함수 `method`
method : 메서드는 다른 함수와 동일하게 정의하지만, 객체 속성에 할당되어있다. 짧은 구문으로 작성 가능하다.
const 객체 = {
속성 : function () {
console.log('a');
},
속성2(){
console.log('b');
},
};
객체.속성(); > a
객체.속성2(); > b
객체 생성 방법
- 리터럴 표기 : `const foo = { name : 'a' };
- 생성자 함수 : `new` 인스턴스 생성
- 속성이 동일한 객체를 여러개 탬플릿처럼 생성할 수 있다.
- 생성자 함수를 정의후, 객체 인스턴스를 생성해 사용한다.
- 함수명은 꼭 대문자!
function Person(name, age){
this.name = name;
this.age = age;
}
new Person('dah',11);
- `Object.create(객체)` : 생성자 함수처럼 동일한 속성값을 갖는 객체를 생성할 수 있다.
- `const Joy = Object.create(Person);` : Person과 동일한 키값을 가지는 Joy 객체 생성된다.
object 빌트인, 객체 속성 control
- 생성자 : new Object()
- 정적 method : 객체 자체 method, 객체 속성 method, 객체 속성 값 method
- 인스턴스 method : `hasOwnProperty()` `isPrototypeOf()` `propertyIsEnumerable()` `toLocaleString()` `toString()` `valueOf()`
정적 method
객체 자체 method : 복사, 신규생성, 고정, 상태확인. 배열로 반환, prototype 관한 메서드
객체 속성 method : 추가, 서술자, 열거
객체 속성의 값 method : 비교 is, 열거 values
객체 mutable, 참조형태 > 참조된 객체들도 같이 변경되는 문제점 발생
속성 동적생성 > 객체 밖에서 마침표 표기법으로 선언해주어도 생성된다
속성 나열
- for-in : 모든 열거 가능한 속성을 순회한다(체인의 속성까지)
- Object.keys(객체) : 객체 자신의 열거 가능한 속성 이름을 배열로 반환한다
- Object.getOwnPropertyNames(객체) : 객체 자신만의 모든 속성을 배열로 반환한다.
속성 삭제 : `delete Person.name;` 반환값 boolean
객체 비교와 Reference
객체는 Reference type 참조타입을 가진다. 모든 연산이 참조값 즉 메모리 주소로 처리된다.
- 객체 비교
- 객체 속성과 값이 같은 객체여도, 참조값이 다르므로 서로 다르다.
- 자기자신의 비교에서만 true
- 객체 복사 : 동일한 객체를 생성해야할 때
- 다른 변수에 그대로 할당할 경우, 동일한 메모리 주소 address 만 할당된다
- 둘중 하나의 객체를 수정하면, 동일한 메모리주소이기 때문에 결과적으로 2개 다 변경된다
- side effect 를 고려해 복사하는 방법
- 얕은 복사 Shallow copy
- 복사된 객체의 속성 중 하나라도 같은 참조를 하고있을 경우
- 참조타입이 없는경우 권장되는 방법
- 정적 메소드 `Object.assign({}, 복사할 객체)`
- spread 연산자 `{ ...복사할 객체 }` 객체 속성 열거
- 깊은 복사 Deep copy
- 하나라도 같은 참조를 하고 있는 속성이 없다면, 깊은복사로 복사되었다고 한다
- 참조타입을 신구로 해야하기 때문에 순회하면서 참조를 확인하는 방법
- 재귀함수를 이용한 복사 : 라이브러리 lodash - cloneDeep
- JSON.stringify : 객체 > 문자열로 변환(참조가 끊어진다) > `parse` 객체로 변환 > 연산의 부담이 발생한다
- 얕은 복사 Shallow copy
객체의 종류
- 표준 빌트인 객체 ECMAScript
- 기초객체 : Object, Function, Boolean, Symbol
- 오류 객체 : Error
- 숫자 및 날짜 : Number, BigInt, Math, Date
- 텍스트 처리 : Regex(정규표현식), String(문자열)
- Collection
- Index based Collection : Array
- Key based Collection : Map, Set, ...
- 구조화된 데이터 : JSON
- 제어 추상화 객체 : Promise, Generator, AsyncFunction,...
- 기초객체 : Object, Function, Boolean, Symbol
- 호스트 객체(브라우저 기준) DOM API
달러($) 기호는 제이쿼리를 의미하고, 제이쿼리에 접근할 수 있게 해주는 식별자이다.
☑️ 가계부 구현
CurrentAsset
TODO: 숫자에 콤마 작성
TODO: currentFunds가 없는 경우
$currentAssetValue.textContent = store.currentFunds?.toLocaleString() ?? "-";
currentFund 숫자를 replace로 쪼개지말고, parameter 값이 비어있을때, 숫자의 천단위에 콤마 , 를 찍어주는 JS 메소드 사용한다.
optional 옵셔널 체이닝 으로 currentFunds가 없는 경우 대응하지 않는다를 ? 문법으로 처리한다.
if 문, 삼항연산자로도 사용가능
Validation
TODO: 값이 존재하는지
"". null, undefined
export function validateRequired({ category, description, price }) {
return (
Boolean(category) && Boolean(description) && Boolean(price) && price > 0
);
}
TODO: 금액이 현재 자산보다 이하인지
export function validatePrice(currentFunds, currentAmount) {
return currentFunds >= currentAmount;
}
addNewHistory
TODO
- store의 detailList 새로 갱신
- 존재할경우 push, 아닐경우 빈객체 갱신
- store.currentFunds 새로 갱신
- 현재자산 - amount
export function addNewHistory(newHistory) {
try {
//store의 detailList 새로 갱신
if (store.detailList[store.todayId]) {
store.detailList[store.todayId] = store.detailList[store.todayId].push(newHistory);
} else {
store.detailList[store.todayId] = [newHistory];
}
//store.currentFunds 새로 갱신
store.currentFunds -= newHistory.amount;
updateStorage();
return true;
} catch (error) {
alert(error);
return false;
}
}
데이터 매핑
TODO: 데이터 매핑
배열을 amp 돌리면 > 배열 형태로 반환됨
원하는 형태는 문자열이기 때문에 join() 메소드 사용
값들을 매핑하기 위해서 map 안 요소에 들어가야할 항목들 작성하고, html에 띄워준다
$sectionHistory.innerHTML = store.dateList
.map(({ date, id: dateId }) => {
const detail = store.detailList[dateId];
if (!detail?.length) return "";
return `<article class="history-per-day">
<p class="history-date">2021년 12월 1일</p>
${detail
.map(({ description, category, amount, fundsAtTheTime, createAt }) => {
return `<section class="history-item">
<section class="history-item-column">
<div class="create-at">${createAt}</div>
<div class="history-detail">
<div class="history-detail-row history-detail-title">
<p>${description}</p>
</div>
<div class="history-detail-row history-detail-subtitle">
<p>${category}</p>
<p>
${amount}
<span>원</span>
</p>
</div>
</div>
<div class="delete-section">
<button class="delete-button">🗑</button>
</div>
</section>
<section class="history-item-caption">
<p>
<span>남은 자산</span>
<span>${fundsAtTheTime}</span>
<span>원</span>
</p>
</section>
</section>`;
})
.join("")}
</article>`;
})
.join("");
}
TODO: 항목의 시간 포맷 변경: HH:mm
DateISOType 로 작성되어있기 때문에, 예쁘게 띄우려면 데이터 가공이 필요하다!
- iso 타입을 date 객체로 변환
const time = new Date(createAt).toLocaleTimeString("ko-kr", {
timeStyle: "short",
hourCycle: "h24",
});
TODO: 오름차순으로 목록 나열
소비내역 리스트 최신항목이 가장 상단에!
${detail
.sort((a, b) => b.id - a.id)
TODO: 금액 콤마 포맷 맞추기
${amount.toLocaleString()}
${fundsAtTheTime.toLocaleString()}
removeHistory
TODO:
- store의 detailList 새로 갱신
기존 배열 > 삭제할 요소를 제거한 배열 > 재할당
store.detailList[dateId] = store.detailList[dateId].filter(({ id }) => {
return id !== Number(itemId);
});
- store.currentFunds 새로 갱신
반복문 돌아야 하기 때문에, 앞에 갱신 배열 도는 김에 같이 돌기!
삭제할 요소일때 > 전체 금액에 더해서 갱신
store.detailList[dateId] = store.detailList[dateId].filter(
({ id, amount }) => {
if (id === Number(itemId)) {
store.currentFunds += amount;
}
return id !== Number(itemId);
}
);
삭제 버튼 클릭 할때 > 각 요소 id 매핑
<button class="delete-button" data-dateid=${dateId} data-itemid=${id}>🗑</button>