[강의] 자바스크립트의 문법과 표현

김하은·2023년 11월 9일
0

코드잇 강의 정리

목록 보기
38/60

문장과 표현식

  • 모든 자바스크립트 코드는 모두 문장과 표현식으로 구성되어 있음

문장 (statements): 어떤 동작이 일어나도록 작성된 최소한의 코드 덩어리

표현식 (expressions): 결과적으로 하나의 값이 되는 모든 코드

// 어떤 하나의 값을 그대로 작성하는 것
5 // 5
'string' // string
----------------------------------------------
// 연산자를 이용한 연산식
5 + 7 // 12
'I' + ' Love ' + 'Codeit' // I Love Codeit
true && null // null
---------------------------------------------
const title = 'JavaScript';
const codeit = {
  name: 'Codeit'
};
const numbers = [1, 2, 3];

// 선언된 변수를 호출하거나, 객체의 프로퍼티에 접근하는 것
typeof codeit // object
title // JavaScript
codeit.name // Codeit
numbers[3] // undefined
  • 표현식은 보통 문장의 일부로 쓰이지만, 그 자체로 문장일 수도 있음
// 할당 연산자는 값을 할당하는 동작도 하지만, 할당한 값을 그대로 가지는 표현식이다.
title = 'JavaScript'; // JavaScript

// 함수 호출은 함수를 실행하는 동작도 하지만, 실행한 함수의 리턴 값을 가지는 표현식이다.
sayHi(); // sayHi 함수의 리턴 값

// console.log 메소드는 콘솔에 아규먼트를 출력하는 동작도 하지만, undefined 값을 가지는 표현식이다.
console.log('hi'); // undefined
  • 문장은 다시 표현식인 문장과, 표현식이 아닌 문장으로 나눌 수 있음
    • 이 둘을 구분하는 가장 간단한 방법은 구분하고자 하는 문장을 변수에 할당하거나, 어떤 함수의 아규먼트로 전달해보는 것임
    • 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); // 1 2 3
console.log(1, 2, 3); // 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); // (6) [1, 2, 3, 4, 5, 6]
  • spread 구문은 하나로 묶인 값을 여러 개의 값으로 펼친 개념이기 때문에 하나의 값으로 평가되는 게 아니라 여러 값의 목록으로 평가됨
    • 하나의 값을 가진 배열을 펼치면 오류가 발생함
  • 배열은 객체로 펼칠 수 있지만 객체는 배열로 펼칠 수 없음
const members = ['태호', '종훈', '우재'];
const newObject = { ...members };

console.log(newObject); // {0: "태호", 1: "종훈", 2: "우재"}

const topic = {
  name: '모던 자바스크립트',
  language: 'JavaScript', 
}
const newArray = [...topic]; // TypeError!

객체 Spread하기

  • ES2015에서 Spread 구문이 처음 등장했을 때는 일반 객체에는 사용할 수가 없었지만 ES2018에서 일반 객체에도 Spread 구문을 사용할 수있는 표준이 등장함
  • 대부분의 브라우저에서는 객체를 복사하거나 기존의 객체를 가지고 새로운 객체를 만들 때 Spread 구문 활용할 수가 있음
  • 중괄호 안에서 객체를 spread 하게되면, 해당 객체의 프로퍼티들이 펼쳐지면서 객체를 복사할 수가 있게 됨
const codeit = { 
  name: 'codeit', 
};

const codeitClone = { 
  ...codeit, // spread 문법!
};

console.log(codeit); // {name: "codeit"}
console.log(codeitClone); // {name: "codeit"}
  • 다른 객체가 가진 프로퍼티에 다른 프로퍼티를 추가해서 새로운 객체를 만들 때 활용할 수 있음
const latte = {
  esspresso: '30ml',
  milk: '150ml'
};

const cafeMocha = {
  ...latte,
  chocolate: '20ml',
}

console.log(latte); // {esspresso: "30ml", milk: "150ml"}
console.log(cafeMocha); // {esspresso: "30ml", milk: "150ml", chocolate: "20ml"}
  • 배열을 Spread 하면 새로운 배열을 만들거나 함수의 아규먼트로 쓸 수 있었지만, 객체로는 새로운 배열을 만들거나 함수의 아규먼트로 사용할 수는 없으므로 반드시 객체를 표현하는 중괄호 안에서 활용해야 함
const latte = {
  esspresso: '30ml',
  milk: '150ml'
};

const cafeMocha = {
  ...latte,
  chocolate: '20ml',
}

[...latte]; // Error

(function (...args) {
  for (const arg of args) {
    console.log(arg);
  }
})(...cafeMocha); // Error

모던한 프로퍼티 표기법

  • 객체의 프로퍼티를 간결하게 표현하는 법
  • 변수나 함수룰 활용해서 프로퍼티를 만들 때 프로퍼티 네임과 변수나 함수 이름이 같다면 다음과 같이 축약해서 사용할 수 있음
function sayHi() {
  console.log('Hi!');
}

const title = 'codeit';
const birth = 2017;
const job = '프로그래밍 강사';

const user = {
  title, 
  birth, 
  job, 
  sayHi,
};

console.log(user); // {title: "codeit", birth: 2017, job: "프로그래밍 강사", sayHi: ƒ}
  • 메소드를 작성할 때도 다음과 같이 function 키워드를 생략할 수가 있음
const user = {
  firstName: 'Tess',
  lastName: 'Jang',
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  },
};

console.log(user.getFullName()); // Tess Jang
  • 계산된 속성명(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.catnull 혹은 undefined가 아니라는 것을 검증하고 접근해야 에러를 방지할 수가 있음
const user2 = {
  name: 'Young',
}

console.log(user2.cat); // undefined
printCatName(user2); // TypeError: Cannot read property 'name' of undefined
  • 이런 상황에 if문 보다 코드를 간결하게 사용할 수 있는 문법이 바로 옵셔널 체이닝(Optional Chaining)임
  • 물음표와 마침표를 붙여 사용하는 부분이 바로 옵셔널 체이닝 연산자?.
  • 만약 옵셔널 체이닝 연산자 왼편의 프로퍼티 값이 undefined 또는 null이 아니라면 그다음 프로퍼티 값을 리턴하고 그렇지 않은 경우에는 undefined를 반환하는 문법
function printCatName(user) {
  console.log(user.cat?.name);
}
  • null 병합 연산자와 함께 활용한 예시
function printCatName(user) {
  console.log(user.cat?.name ?? '함께 지내는 고양이가 없습니다.');
}

const user2 = {
  name: 'Young',
}

printCatName(user2); // 함께 지내는 고양이가 없습니다.

Destructuring (구조 분해)

  • 배열의 요소나 객체의 프로퍼티 값들을 개별적인 변수에 따로 따로 할당해서 다룰 수가 있음

배열

// Array 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); //효준

객체

// Object Destructuring
const macbookPro = {
  title: '맥북 프로 16형',
  price: 3690000,
};

const { title, price } = macbookPro;

console.log(title); // 맥북 프로 16형
console.log(price); // 3690000
  • 간결하게 프로프티 네임 자체를 변수처럼 사용하고자 할 때 구조 분해를 사용함
  • 할당 연산자 왼편에 선언된 변수의 이름과 똑같은 프로퍼티 네임이 있으면 변수 이름에 그 값이 할당되는 방식으로 동작함
  • 객체에 존재하지 않는 프로퍼티 네임으로 변수가 선언 돼 있으면 undefined 값이 할당 됨
  • 할당 연산자로 기본값을 지정해 줄 수 있음
  • 마침표 세개 ...를 통해서 앞쪽 프로퍼티가 유효한 부분들은 모두 할당한 다음에 남은 프로퍼티들을 하나의 객체로 모아서 변수에 담을 수 있음
  • 프로퍼티 네임과 다른 새로운 이름의 변수를 선언하고 싶다면 :를 이용하면 됨
    const { title: product, price } = macbookPro;
    • 객체 내부의 프로퍼티 네임이 변수이름으로 사용할 수 없는 경우에 사용하면 좋음
    • computed property name을 사용할 수도 있음
const propertyName = 'title'
const { [propertyName]: project } = macbook;

함수와 Destructuring

  • 함수에서 default parameter, rest parameter를 다루듯이 Destructuring 문법을 활용할 때도 기본값과 rest 문법을 활용할 수 있음
// Array Destructuring
const members = ['코딩하는효준', '글쓰는유나', undefined, '편집하는민환', '촬영하는재하'];
const [macbook, ipad, airpod = '녹음하는규식', ...coupon] = members;

console.log(macbook); // 코딩하는효준
console.log(ipad); // 글쓰는유나
console.log(airpod); // 녹음하는규식
console.log(coupon); // (2) ["편집하는민환", "촬영하는재하"]

// Object Destructuring
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); // 맥북 프로 16형
console.log(price); // 3690000
console.log(color); // silver
console.log(rest); // {memory: "16 GB 2667 MHz DDR4", storage: "1TB SSD 저장 장치"}
  • 파라미터 부분에서 바로 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);

// DOM 이벤트를 다룰 때 활용하기
const btn = document.querySelector('#btn');

btn.addEventListener('click', ({ target }) => {
  target.classLIst.toggle('checked');
});

// 중첩 객체 구조 분해 -> target 프로퍼티가 객체이므로 중첩된 객체는 :로 한번 더 분해할 수 있음
const btn = document.querySelector('#btn');

btn.addEventListener('click', ({ target: { classList } }) => {
  classLIst.toggle('checked');
});

에러와 에러 객체

  • 자바스크립트에서 에러가 발생하면 그 순간 프로그램 자체가 멈춰버리고 이후의 코드가 동작하지 않기 때문에 에러를 다루는 일은 굉장히 중요함
  • 에러가 발생하면 에러에 대한 정보를 namemessage라는 프로퍼티로 담고 있는 에러 객체가 만들어짐
  • 대표적인 에러 객체는 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) {
  // finally문에서 에러가 발생했을 때 실행할 코드
}

참고 자료

profile
아이디어와 구현을 좋아합니다!

0개의 댓글