문장과 표현식
- 모든 자바스크립트 코드는 모두 문장과 표현식으로 구성되어 있음
문장 (statements): 어떤 동작이 일어나도록 작성된 최소한의 코드 덩어리
표현식 (expressions): 결과적으로 하나의 값이 되는 모든 코드
5
'string'
----------------------------------------------
5 + 7
'I' + ' Love ' + 'Codeit'
true && null
---------------------------------------------
const title = 'JavaScript';
const codeit = {
name: 'Codeit'
};
const numbers = [1, 2, 3];
typeof codeit
title
codeit.name
numbers[3]
- 표현식은 보통 문장의 일부로 쓰이지만, 그 자체로 문장일 수도 있음
title = 'JavaScript';
sayHi();
console.log('hi');
- 문장은 다시 표현식인 문장과, 표현식이 아닌 문장으로 나눌 수 있음
- 이 둘을 구분하는 가장 간단한 방법은 구분하고자 하는 문장을 변수에 할당하거나, 어떤 함수의 아규먼트로 전달해보는 것임
console.log
메소드의 아규먼트로 if문을 전달하거나 someloop
라는 변수에 for 반복문을 할당하게 되면, Error가 발생하게 되는데 조건문이나 반복문은 값으로 평가되지 않고 오로지 문장으로만 평가되기 때문임
let x;
x = 3;
console.log(if (x < 5) {
console.log('x는 5보다 작다');
} else {
console.log('x는 5보다 크다');
});
const someloop = for (let i = 0; i < 5; i++) {
console.log(i);
};
- 특별한 경우를 제외하면 일반적으로 표현식인 문장은 세미콜론으로, 표현식이 아닌 문장은 문장 자체의 코드 블록(중괄호)로 그 문장의 범위가 구분됨
(3 + 4) * 2;
console.log('Hi!');
while(true) {
x++;
}
조건을 다루는 표현식
- 조건 연산자(삼항 연산자): if나 switch 처럼 문장 형태의 코드 블록을 사용하지 않고 보다 간결하게 표현식으로도 조건을 다룰 수 있음
조건 ? truthy 할 때 표현식: falsy 할 때 표현식
const cutOff = 80;
const passChecker = (score) => score > cutOff ? '합격입니다!' : '불합격입니다!';
console.log(passChecker(75));
- 표현식 이기 때문에 조건에 따라 선언하거나 조건에 따라 반복문을 실행할 수 없어서 모든 if문을 대체 할 수는 없음
spread 구문
- 여러 개의 값을 묶어놓은 배열이나 객체와 같은 값은 바로 앞에 마침표 세 개를 붙여서 개별 값으로 펼칠 수가 있음
const numbers = [1, 2, 3];
console.log(...numbers);
console.log(1, 2, 3);
- 배열이나 객체를 복사하거나 혹은 복사해서 새로운 요소들을 추가할 때 유용하게 활용 될 수 있음
const webPublishing = ['HTML', 'CSS'];
const interactiveWeb = [...webPublishing, 'JavaScript'];
console.log(webPublishing);
console.log(interactiveWeb);
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2];
console.log(arr3);
- spread 구문은 하나로 묶인 값을 여러 개의 값으로 펼친 개념이기 때문에 하나의 값으로 평가되는 게 아니라 여러 값의 목록으로 평가됨
- 하나의 값을 가진 배열을 펼치면 오류가 발생함
- 배열은 객체로 펼칠 수 있지만 객체는 배열로 펼칠 수 없음
const members = ['태호', '종훈', '우재'];
const newObject = { ...members };
console.log(newObject);
const topic = {
name: '모던 자바스크립트',
language: 'JavaScript',
}
const newArray = [...topic];
객체 Spread하기
- ES2015에서 Spread 구문이 처음 등장했을 때는 일반 객체에는 사용할 수가 없었지만 ES2018에서 일반 객체에도 Spread 구문을 사용할 수있는 표준이 등장함
- 대부분의 브라우저에서는 객체를 복사하거나 기존의 객체를 가지고 새로운 객체를 만들 때 Spread 구문 활용할 수가 있음
- 중괄호 안에서 객체를 spread 하게되면, 해당 객체의 프로퍼티들이 펼쳐지면서 객체를 복사할 수가 있게 됨
const codeit = {
name: 'codeit',
};
const codeitClone = {
...codeit,
};
console.log(codeit);
console.log(codeitClone);
- 다른 객체가 가진 프로퍼티에 다른 프로퍼티를 추가해서 새로운 객체를 만들 때 활용할 수 있음
const latte = {
esspresso: '30ml',
milk: '150ml'
};
const cafeMocha = {
...latte,
chocolate: '20ml',
}
console.log(latte);
console.log(cafeMocha);
- 배열을 Spread 하면 새로운 배열을 만들거나 함수의 아규먼트로 쓸 수 있었지만, 객체로는 새로운 배열을 만들거나 함수의 아규먼트로 사용할 수는 없으므로 반드시 객체를 표현하는 중괄호 안에서 활용해야 함
const latte = {
esspresso: '30ml',
milk: '150ml'
};
const cafeMocha = {
...latte,
chocolate: '20ml',
}
[...latte];
(function (...args) {
for (const arg of args) {
console.log(arg);
}
})(...cafeMocha);
모던한 프로퍼티 표기법
- 객체의 프로퍼티를 간결하게 표현하는 법
- 변수나 함수룰 활용해서 프로퍼티를 만들 때 프로퍼티 네임과 변수나 함수 이름이 같다면 다음과 같이 축약해서 사용할 수 있음
function sayHi() {
console.log('Hi!');
}
const title = 'codeit';
const birth = 2017;
const job = '프로그래밍 강사';
const user = {
title,
birth,
job,
sayHi,
};
console.log(user);
- 메소드를 작성할 때도 다음과 같이 function 키워드를 생략할 수가 있음
const user = {
firstName: 'Tess',
lastName: 'Jang',
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
};
console.log(user.getFullName());
- 계산된 속성명(computed property name): 대괄호를 활용하면 다양한 표현식으로 프로퍼티 네임을 작성할 수 있음
const propertyName = 'birth';
const getJob = () => 'job';
const codeit = {
['topic' + 'name']: 'Modern JavaScript',
[propertyName]: 2017,
[getJob()]: '프로그래밍 강사',
};
console.log(codeit);
옵셔널 체이닝
- 안전하게 프로퍼티에 접근하는 방법
- 중첩된 객체의 프로퍼티를 다룰 때는
user.cat.name
에 접근하기 전에 user.cat
이 null
혹은 undefined
가 아니라는 것을 검증하고 접근해야 에러를 방지할 수가 있음
const user2 = {
name: 'Young',
}
console.log(user2.cat);
printCatName(user2);
- 이런 상황에 if문 보다 코드를 간결하게 사용할 수 있는 문법이 바로 옵셔널 체이닝(Optional Chaining)임
- 물음표와 마침표를 붙여 사용하는 부분이 바로 옵셔널 체이닝 연산자
?.
임
- 만약 옵셔널 체이닝 연산자 왼편의 프로퍼티 값이
undefined
또는 null
이 아니라면 그다음 프로퍼티 값을 리턴하고 그렇지 않은 경우에는 undefined
를 반환하는 문법
function printCatName(user) {
console.log(user.cat?.name);
}
function printCatName(user) {
console.log(user.cat?.name ?? '함께 지내는 고양이가 없습니다.');
}
const user2 = {
name: 'Young',
}
printCatName(user2);
Destructuring (구조 분해)
- 배열의 요소나 객체의 프로퍼티 값들을 개별적인 변수에 따로 따로 할당해서 다룰 수가 있음
배열
const members = ['코딩하는효준', '글쓰는유나', '편집하는민환'];
const [macbook, ipad, coupon] = members;
console.log(macbook);
console.log(ipad);
console.log(coupon);
- 할당 연산자의 오른쪽이 배열이 아니거나 아무것도 할당하지 않으면 오류가 발생함
- 선언된 변수와 배열의 길이가 같아야 할 필요는 없음 (인덱스에 따라 순서대로 할당됨)
- 가장 마지막 변수에 마침표 세개
...
를 붙여주면 남은 나머지 요소를 마지막 변수의 배열로 할당할 수 있음
- 할당하는 배열의 길이가 선언된 변수의 개수보다 적으면 순서대로 배열 안의 요소가 할당되고 남은 변수에는
undefined
값이 할당 됨
- 기본값을 줄 수 있기 때문에
undefined
값 대신 기본값이 할당되게 할 수도 있음
- 두 변수에 담긴 값을 임시 변수를 생성하지 않고도 교환할 수 있음
let macbook = '효준';
let ipad = '유나';
[macbook, ipad] = [ipad, macbook];
console.log(mackbook);
console.log(ipad);
객체
const macbookPro = {
title: '맥북 프로 16형',
price: 3690000,
};
const { title, price } = macbookPro;
console.log(title);
console.log(price);
- 간결하게 프로프티 네임 자체를 변수처럼 사용하고자 할 때 구조 분해를 사용함
- 할당 연산자 왼편에 선언된 변수의 이름과 똑같은 프로퍼티 네임이 있으면 변수 이름에 그 값이 할당되는 방식으로 동작함
- 객체에 존재하지 않는 프로퍼티 네임으로 변수가 선언 돼 있으면 undefined 값이 할당 됨
- 할당 연산자로 기본값을 지정해 줄 수 있음
- 마침표 세개
...
를 통해서 앞쪽 프로퍼티가 유효한 부분들은 모두 할당한 다음에 남은 프로퍼티들을 하나의 객체로 모아서 변수에 담을 수 있음
- 프로퍼티 네임과 다른 새로운 이름의 변수를 선언하고 싶다면
:
를 이용하면 됨
const { title: product, price } = macbookPro;
- 객체 내부의 프로퍼티 네임이 변수이름으로 사용할 수 없는 경우에 사용하면 좋음
- computed property name을 사용할 수도 있음
const propertyName = 'title'
const { [propertyName]: project } = macbook;
함수와 Destructuring
- 함수에서 default parameter, rest parameter를 다루듯이 Destructuring 문법을 활용할 때도 기본값과 rest 문법을 활용할 수 있음
const members = ['코딩하는효준', '글쓰는유나', undefined, '편집하는민환', '촬영하는재하'];
const [macbook, ipad, airpod = '녹음하는규식', ...coupon] = members;
console.log(macbook);
console.log(ipad);
console.log(airpod);
console.log(coupon);
const macbookPro = {
title: '맥북 프로 16형',
price: 3690000,
memory: '16 GB 2667 MHz DDR4',
storage: '1TB SSD 저장 장치',
};
const { title, price, color = 'silver', ...rest } = macbookPro;
console.log(title);
console.log(price);
console.log(color);
console.log(rest);
- 파라미터 부분에서 바로 Destructuring 문법을 활용할 수 있음
const macbook = {
title: '맥북 프로 16형',
price: 3690000,
color: 'silver',
memeory: '16GB',
storage: '1TB SSD 저장 장치',
display: '16형 Retina 디스플레이',
};
function printSummary({ title, color, price }) {
console.log(`선택한 상품은 '${title}'입니다.`);
console.log(`색상은 ${color}이며,`);
console.log(`가격은 ${price}원 입니다.`);
};
printSummary(macbook);
const btn = document.querySelector('#btn');
btn.addEventListener('click', ({ target }) => {
target.classLIst.toggle('checked');
});
const btn = document.querySelector('#btn');
btn.addEventListener('click', ({ target: { classList } }) => {
classLIst.toggle('checked');
});
에러와 에러 객체
- 자바스크립트에서 에러가 발생하면 그 순간 프로그램 자체가 멈춰버리고 이후의 코드가 동작하지 않기 때문에 에러를 다루는 일은 굉장히 중요함
- 에러가 발생하면 에러에 대한 정보를
name
과 message
라는 프로퍼티로 담고 있는 에러 객체가 만들어짐
- 대표적인 에러 객체는 SyntaxError(코드를 실행 조차 하지 않음), ReferenceError, TypeError임
- 에러 객체의 종류
- 에러 객체를 활용해서 의도적으로 에러를 발생시키는 법
const error = new TypeError('타입 에러가 발생했습니다.');
throw error;
throw new TypeError('타입 에러가 발생했습니다.');
try catch문
try {
} catch (error) {
}
try
문 안에서 실행할 코드를 작성하고, try
문에서 에러가 발생한 경우에 실행할 코드를 catch
문 안에 작성하면 됨
- 이 때
try
문에서 발생한 에러 객체가 catch
문의 첫 번째 파라미터로 전달됨
- 만약,
try
문에서 에러가 발생하지 않을 경우 catch
문의 코드는 동작하지 않음
try {
console.log('에러 전');
const codeit = '코드잇';
console.log(codeit)
codeit = 'codeit';
const language = 'JavaScript';
console.log(language);
} catch (e) {
console.log('에러 후');
console.error(e);
console.log(e.name);
console.log(e.message);
}
try catch 활용하기
- 에러가 발생한 다음에도 프로그램이 멈추지 않게 해서 안정적인 프로그램을 만들 수 있음
- 블록 스코프를 갖는 변수를 사용할 때 각 코드 블록 안에서의 스코프를 잘 생각해야 함
- 실행이 가능한 코드 내에서의 에러를 다룸
- Exception: SyntaxError(코드를 실행 조차 하지 않음)와 달리 실행이 가능한 코드에서 발생한 에러
- Exception Handling: 실행가능한 코드에서 발생한 에러를 다룸
finally문
try...catch
finally라는 코드 블록을 하나 더 가질 수 있음
finally
문은 try...catch
문이 끝난 다음에 최종적으로 실행될 코드를 다룰 때 활용함
try
문에서 에러가 발생하지 않는다면 try
문의 코드가 모두 실행된 다음에, try
문에서 에러가 발생한다면 catch
문의 코드가 모두 실행된 다음 실행할 코드를 finally
문에 작성하면 됨
try
문에서 어떤 코드를 실행할 때 에러 여부와 상관 없이 항상 실행할 코드를 작성하는 것
try {
} catch (err) {
} finally {
}
function printMembers(...members) {
for (const member of members) {
console.log(member);
}
}
try {
printMembers('영훈', '윤수', '동욱');
} catch (err) {
alert('에러가 발생했습니다!');
console.error(err);
} finally {
const end = new Date();
const msg = `코드 실행을 완료한 시각은 ${end.toLocaleString()}입니다.`;
console.log(msg);
}
finally
문에서 에러가 발생할 경우에는 다시 그 위에 있는 catch
문으로 넘어가진 않음
- 만약
finally
문에서도 에러 처리가 필요한 경우에는 아래 처럼 try...catch
문을 중첩해서 활용할 수 있음
try {
try {
} catch (err) {
} finally {
}
} catch (err) {
}
참고 자료