띄엄띄엄 알고있던 JavaScript 문법을 제대로 공부하며 정리하였습니다.
앞으로 개발할 때 자주 찾아보며 익숙해지려고 합니다.
내용이 길어 기본과 심화로 나누어 포스팅합니다.
객체#
객체 안의 함수 (메소드)#
const banana = {
name: 'banana',
display: function () {
console.log(`${this.name}: 🍌`)
}
}
banana.display();
생성자 함수#
// 생성자
function Workout(name, emoji) {
this.name = name;
this.emoji = emoji;
this.doit = () => {
console.log(`${this.name}: ${this.emoji}`);
};
}
const running = new Workout('running', '🏃♂️');
const weight_lifting = new Workout('weight lifting', '🏋️♂️');
console.log(running);
running.doit()
weight_lifting.doit();
클래스#
생성, 사용#
원래 JavaScript에서는 ProtoType을 기반으로 객체를 생성하나, ES6 부터는 Syntax Sugar로 클래스를 사용합니다. (내부적으로는 ProtoType 기반으로 객체를 만듭니다.)
메서드는 function 키워드를 사용하지 않습니다. (생략 또는 Arrow 함수 사용)
// 객체를 만들수 있는 템플릿
// 1. 생성자
// 2. 클래스 ✨
class Workout {
constructor(name, emoji) {
this.name = name;
this.emoji = emoji;
}
/* Arrow 함수 사용
doit = () => {
console.log(`${this.name}: ${this.emoji}`);
};
*/
// function 키워드 생략
doit() => {
console.log(`${this.name}: ${this.emoji}`);
};
}
const weight_lifting = new Workout('Weight-lifting', '🏋️♂️');
const running = new Workout('Running', '🏃♂️');
console.log(weight_lifting);
console.log(running);
console.log(weight_lifting.name);
console.log(running.emoji);
weight_lifting.doit();
Static#
static 키워드를 사용하면 클래스 레벨에서 사용 가능한 속성, 메서드가 됩니다.
// static 정적 프로퍼티, 메서드
class Workout {
static SET_COUNT = 4;
constructor(name, emoji) {
this.name = name;
this.emoji = emoji;
}
// 클래스 레벨의 메서드
static getTimer() {
console.log('This is Timer');
return "Count 1 min";
}
// 인스턴스 레벨의 메서드
doit = () => {
console.log(`${this.name}: ${this.emoji}`);
};
}
const timer = Workout.getTimer();
console.log(timer);
console.log(`Set count: ${Workout.SET_COUNT} | Timer: ${timer}`)
field 접근 제어자 #
#
속성명 앞에 # 을 붙이면 클래스 외부에서 접근이 불가능하게 됩니다.
// 접근제어자 = 캡슐화
// private(#), public(기본), protected
class Workout {
#name;
#emoji;
#type = '운동';
constructor(name, emoji) {
this.#name = name;
this.#emoji = emoji;
}
#display = () => {
console.log(`${this.#name}: ${this.#emoji}`);
};
}
const running = new Workout('Running', '🏃♂️');
//running.#name = '달리기'; // #field는 외부에서 접근이 불가능함
console.log(running);
Getter, Setter#
메서드 앞에 function 대신 get
을 사용하면 getter로 동작하여 ()
없이 호출할 수 있습니다.
메서드 앞에 set
을 사용하면 setter로 동작하여 ()
없이 호출할 수 있습니다.
// 접근자 프로퍼티 (Accessor Property)
class Player {
constructor(sport, nation, firstName, lastName, age) {
this.sport = sport;
this.nation = nation;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
get fullName() {
return `${this.lastName} ${this.firstName}`;
}
set fullName(value) {
console.log('set', value)
}
}
const player = new Player('농구', '대한민국', '철수', '김', 25);
player.firstName = '수현';
// () 없이, 프로퍼티처럼 값을 가져올 수 있습니다. ⚡
console.log(player.fullName);
// () 없이, 프로퍼티처럼 값을 할당할 수 있습니다. ⚡
player.fullName = '김민수';
console.log(player.fullName);
상속 (Extends)#
introduce
메서드에서super
접근 문제:introduce
메서드가Person
클래스와Player
클래스에서 모두 화살표 함수로 정의되어 있습니다. 화살표 함수는 자신이 정의된 문맥의this
를 사용하므로,super
를 사용할 수 없습니다.
// 상속 (Extends)
class Person {
#firstName;
#lastName;
#age;
#nation;
constructor(firstName, lastName, age, nation) {
this.#firstName = firstName;
this.#lastName = lastName;
this.#age = age;
this.#nation = nation;
}
get info() {
return `- Name: ${this.#lastName} ${this.#firstName}
- Age: ${this.#age}
- Nation: ${this.#nation}`;
}
set firstName(value) {
this.#firstName = value;
console.log('Set first name: ', value);
}
set lastName(value) {
this.#lastName = value;
console.log('Set last name: ', value);
}
set age(value) {
this.#age = value;
console.log('Set age: ', value);
}
set nation(value) {
this.#nation = value
console.log('Set nation: ', value);
}
introduce() {
console.log(`Hi, I'm ${this.#firstName}.`);
}
/*
introduce() {
console.log(`Hi, I'm ${this.#firstName}.`);
}
*/
}
class Player extends Person {
#sport;
#emoji;
constructor(firstName, lastName, age, nation, sport, emoji) {
super(firstName, lastName, age, nation);
this.#sport = sport;
this.#emoji = emoji;
}
get playerInfo() {
return `${super.info}
- Sport: ${this.#sport}
- Emoji: ${this.#emoji}
`;
}
set sport(value) {
this.#sport = value;
console.log('Set sport: ', value);
}
set emoji(value) {
this.#emoji = value;
console.log('Set emoji: ', value);
}
// Player 클래스의 메서드를 오버라이딩
introduce() {
super.introduce();
console.log(`I'm ${this.#sport} ${this.#emoji} player`);
}
}
const kobe = new Player('Kobe', 'Bryant', 30, 'USA', 'Basketball', '🏀');
console.log(kobe);
kobe.sport = '농구';
console.log(kobe.playerInfo);
kobe.introduce();
결과
Player {}
Set sport: 농구
- Name: Bryant Kobe
- Age: 30
- Nation: USA
- Sport: 농구
- Emoji: 🏀
Hi, I'm Kobe.
I'm 농구 🏀 player
내장객체#
얕은 복사 (Shallow copy)#
객체는 대입 시 주소가 전달됩니다.
아래와 같이 배열에 담긴 객체를 여러 변수에 할당하여도, 변수에는 객체의 주소들이 배열 형태로 담겨지게 됩니다.
객체 자체를 변경하면, 해당 객체의 주소를 가진 변수들은 모두 변경된 값을 출력하게 됩니다.
const lift = { name: '🏋️♂️', intense: 3, owner: { name: 'ruff' } };
const running = { name: '🏃♂️', intence: 1 };
const climbing = { name: '🧗♂️', intence: 2 };
const workout1 = [lift, running];
const workout2 = Array.from(workout1);
console.log('workout1', workout1);
console.log('workout2', workout2);
workout2.push(climbing);
console.log('workout1', workout1);
console.log('workout2', workout2);
running.intence = 2;
console.log('workout1', workout1);
console.log('workout2', workout2);
고차함수 (Higher order function)#
Sample#
const workout = ['🏋️♂️', '⛹️♀️', '🏃♂️', '🧗♂️'];
foreach#
fruits.forEach(function (value) {
console.log(value);
});
workout.forEach((value) => console.log(value));
find#
const lift = { name: '🏋️♂️', intence: 3 };
const basketball = { name: '⛹️♀️', intence: 2 };
const running = { name: '🏃♂️', intence: 2 };
const myRoutine = [lift, basketball, running];
let result = myRoutine.find((item) => item.name === '🏋️♂️');
console.log(result);
findIndex#
result = myRoutine.findIndex((item) => item.name === '⛹️♀️');
console.log(result);
some#
배열의 아이템들이 콜백 함수내 조건에 맞는지 확인하여, 하나의 요소라도 일치하면 True 출력
// 배열의 아이템들이 부분적으로 조건(콜백함수)에 맞는지 확인
result = myRoutine.some((item) => item.name === '🏋️♂️');
console.log(result);
every#
배열의 아이템들이 콜백 함수내 조건에 맞는지 확인하여, 모든 요소가 일치하면 True 출력
result = myRoutine.every((item) => item.name === '🏋️♂️');
console.log(result);
filter#
배열의 아이템 중 콜백 함수 내 조건에 맞는 아이템들을 새로운 배열로 만들어 리턴합니다.
result = myRoutine.filter((item) => item.name === '⛹️♀️');
console.log(result);
Map#
배열 내 모든 아이템에 콜백 함수를 적용시켜 새로운 배열을 리턴합니다.
const nums = [1, 2, 3, 4, 5];
result = nums.map((item) => {item * 3});
// result = nums.map((item) => {return item * 3;});
// 중괄호{} 사용 시에는 return을 써야함.
console.log(result);
result = nums.map((item) => {
if (item % 2 === 1) {
return item * 3;
} else {
return item;
}
});
console.log(result);
flatMap#
배열의 각 요소에 주어진 콜백 함수를 적용한 다음 결과를 한 단계씩 평탄화하여 형성된 새 배열을 반환합니다.
const nums = [1, 2, 3, 4, 5];
result = nums.map((item) => [1, 2]);
console.log(result);
result = nums.flatMap((item) => [1, 2]);
console.log(result);
result = ['power', 'lifting'].flatMap((text) => text.split(''));
console.log(result);
const arr1 = [1, 2, 1];
const result = arr1.flatMap((num) => (num === 2 ? [2, 2] : 1));
console.log(result);
// Expected output: Array [1, 2, 2, 1]
sort#
원본 배열을 정렬합니다.
const workout = ['golf', 'lifting', 'climbing'];
workout.sort();
console.log(workout);
const nums = [0, 2, 4, 5, 14, 22];
nums.sort();
console.log(nums);
// 콜백 함수를 입력하지 않는 경우, String 기반으로 정렬
// [ 0, 14, 2, 22, 4, 5 ]
numbers.sort((a, b) => a - b);
console.log(numbers);
// [ 0, 2, 4, 5, 14, 22 ]
// a - b : 오름차순
// b - a : 내림차순
reduce#
배열의 각 요소에 대해 주어진 리듀서 (reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(accumulator, currentValue) => accumulator + currentValue,
initialValue,
);
console.log(sumWithInitial);
// Expected output: 10
Iterable#
Iterator#
순회가 가능한 객체
Symbol.iterator: IterableIterator<T>;
심볼 정의를 가진 객체IterableIterator<T>
객체를 리턴하는 함수
const arr = [4, 5, 6];
// 모두 이터레이터를 리턴함
console.log(arr.keys());
console.log(arr.entries());
console.log(arr.values());
// 아이템이 있는 만큼 출력, for of 를 주로 사용
const iter = arr.values();
while (true) {
const item = iter.next();
if (item.done) break;
console.log(item.value);
}
// 아이템이 있는 만큼 출력, 주로 사용됨
for (let item of arr) {
console.log(item);
}
for (let item of arr.values()) {
console.log(item);
}
// Iterable 하지않은 객체는 for in 사용
const obj = { name: 'ruff', age: 30 };
for (const key in obj) {
console.log(obj[key]);
}
Generator#
Iterable 한 객체를 생성하는 함수
function*
키워드를 사용하여 생성합니다.
function* multiGen() {
try {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i ** 10;
}
} catch (error) {
console.log(error);
}
}
const multi = multiGen();
let next = multi.next();
console.log(next.value, next.done);
// multi.return();
multi.throw('error occurred!');
next = multi.next();
console.log(next.value, next.done);
Spread#
전개 구문
Iterable 한 객체는 모두 Spread 될 수 있습니다.
// 함수 입력
function sum1(x, y, z) {
return x + y + z;
}
const nums = [1, 2, 3];
console.log(sum1(...nums));
// 나머지 입력
function sum2(first, second, ...numbers) {
console.log(numbers);
}
sum2(1, 2, 0, 1, 2, 4);
// [ 0, 1, 2, 4 ]
// 객체 이어붙이기
const ruff = { name: 'Ruff', age: 30, home: { address: 'Seoul' } };
const updated = {
...ruff,
job: 'Security Engineer',
};
console.log(ruff );
console.log(updated );
// 배열 이어붙이기
const workout1 = ['🚴♂️', '🧗♂️'];
const workout2 = ['🏋️♂️', '⛹️♂️'];
let arr = workout1.concat(workout2);
console.log(arr );
arr = [...workout1, '🏃♂️', ...workout2];
console.log(arr);
구조 분해 할당 (Destructuring Assignment)#
구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.
// 배열
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// [30, 40, 50]
// Default 값 설정 가능
const point = [4, 5];
const [y, x, z = 0] = point;
console.log(x);
console.log(y);
console.log(z);
// 함수 리턴 값으로 받을 수 있음
**function myRoutine() {
return ['weight', 'running'];
}
// 함수 입력으로 객체를 분해하여 받으면, 함수에 입력 시 객체명을 입력해도 됨
// 단 객체 내 요소명이 동일해야 함.
const benchPress = { name: 'Barbell Bench Press', sets: 4, reps: 10 };
function workout({ name, sets, reps }) {
console.log('종목:', name);
console.log('세트:', sets);
console.log('횟수:', reps);
}
workout(benchPress);
// : 를 이용하여 변수명을 변경할 수 있음
// 나머지 요소에는 Default 값을 설정할 수 있음
const { name, sets, reps: counts, topset = true } = benchPress;
console.log(name);
console.log(sets);
console.log(counts);
console.log(topset);
// 함수의 인자가 정확한 객체의 요소를 가리키면,
// 객체 입력 시 해당하는 요소가 입력됨
const benchPress = {
name: 'Barbell Bench Press',
routine: {
sets: 4,
reps: 10
}
};
function showReps({ routine: { reps } }) {
console.log('횟수:', reps);
}
showReps(benchPress);**
Map#
const map = new Map([
['leg1', 'Squat'],
['leg2', 'Lunges'],
['leg3', 'Leg curls'],
])
console.log(map);
// 크기 확인
console.log(map.size);
// 요소 존재 여부 확인
console.log(map.has('leg1'));
console.log(map.has('leg9'));
// 순회
map.forEach((value, key) => console.log(key, value));
console.log(map.keys());
console.log(map.values());
console.log(map.entries());
// 찾기
console.log(map.get('leg1'));
console.log(map.get('leg2'));
console.log(map.get('leg9'));
// 추가
map.set('leg4', 'Leg extensions');
console.log(map);
// 삭제
map.delete('leg4');
console.log(map);
// 전부삭제
map.clear();
console.log(map);
오브젝트와의 차이점
const key = { name: 'milk', price: 10 };
const milk = { name: 'milk', price: 10, description: '맛있는우유' };
const obj = {
[key]: milk,
};
console.log(obj);
const map2 = new Map([[key, milk]]);
console.log(map2);
console.log(obj[key]);
console.log(map2[key]);
console.log(map2.get(key));
{
'[object Object]': { name: 'milk', price: 10, description: '맛있는우유' }
}
Map(1) {
{ name: 'milk', price: 10 } => { name: 'milk', price: 10, description: '맛있는우유' }
}
{ name: 'milk', price: 10, description: '맛있는우유' }
undefined
{ name: 'milk', price: 10, description: '맛있는우유' }
Symbol#
유일한 키를 생성할 수 있음
const map = new Map();
// const key1 = 'key';
// const key2 = 'key';
const key1 = Symbol('key');
const key2 = Symbol('key');
map.set(key1, 'Hello');
console.log(map.get(key2));
console.log(key1 === key2);
동일한 이름으로 하나의 키를 사용하고 싶다면, Symbol.for 전역 심벌 레지스트리 (Global Symbol Registry)에 저장되며 입력 받은 값으로 직접 접근하지 못함
const k1 = Symbol.for('key');
const k2 = Symbol.for('key');
console.log(k1 === k2);
console.log(Symbol.keyFor(k1));
console.log(Symbol.keyFor(key1));
const obj = { [k1]: 'Hello', [Symbol('key')]: 1 };
console.log(obj);
console.log(obj[k1]);
console.log(obj[Symbol('key')]);
||
&&
연산자#
const obj1 = { name: 'Ben' };
const obj2 = { name: 'Tom', trainer: 'Ruff' };
let result = obj1 && obj2;
console.log(result);
// obj1가 truthy 값이면 obj2를 반환하고 obj1이 falsy 값이면 obj1을 반환
result = obj1 || obj2;
console.log(result);
// obj1이 truthy 값이면 obj1을 반환하고 obj1이 falsy 값이면 obj2를 반환
활용#
null 또는 undefined인 경우 확인
let item = { price: 1 };
const price = item && item.price;
console.log(price);
기본값 설정
default parameter
: null과 undefined인 경우
||
: falshy한 경우 할당 e.g. 0, -0, null, undefined, ''
// default parameter는 null과 undefined인 경우
// || falshy한 경우 설정(할당): 0, -0, null, undefined, ''
function print(message = 'Hi') {
const text = message || 'Hello';
console.log(text);
}
Optional chaining#
null 또는 undefined을 확인
// ?.
let shoulder_press = { reps: 10 };
const count = shoulder_press?.reps;
console.log(count);
Nullish#
??
: null, undefined
||
: falshy한 경우 설정(할당) 0, -0, ''
let num = 0;
console.log(num || '-1');
console.log(num ?? '-1');
Error handdling#
try
catch
finally
#
function search(path) {
// throw new Error('파일 경로를 찾을 수 없음');
return '검색 결과';
}
function analysis(path) {
let content;
try {
content = search(path);
} catch (error) {
console.log(error);
content = '기본내용';
} finally {
console.log('성공하든 실패하든 마지막으로 리소스를 정리할 수 있음!');
}
const result = 'Result: ' + content;
return result;
}
const result = analysis('경로');
console.log(result);
Error bubbling#
throw
를 이용해 Error을 다음 로직으로 넘길 수 있습니다.
// Bubbling up, Propagating 🧼
function a() {
throw new Error('Error!!!');
}
function b() {
try {
a();
} catch (error) {
console.log('Over head!!!');
throw error;
}
}
function c() {
b();
}
try {
c();
} catch (error) {
console.log('Catched!!!');
}
console.log('Done!!!');
Module#
다른 js 파일 import 는 아래와 같습니다.
import { workout as increase1 } from './workout.js';
import { workout, getCount } from './workout.js';
import * as counter from './workout.js';
HTML 파일에서는 아래와 같이 import 합니다.
<script type="module" src="workout.js"></script>
<script type="module" src="main.js"></script>
아래는 예제입니다.
let reps = 0;
export function workout() {
reps++;
console.log(reps);
}
export function getCount() {
return reps;
}
// import { workout as increase1 } from './workout.js';
// import { workout, getCount } from './workout.js';
import * as counter from './workout.js';
counter.increase();
counter.increase();
counter.increase();
console.log(counter.getCount());
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="module" src="workout.js"></script>
<script type="module" src="main.js"></script>
</head>
<body></body>
</html>
Promise#
기본#
resolve에는 성공 (then) 호출 시 수행할 로직, reject에는 실패 (catch) 시 실행할 로직을 작성합니다.
function plank(seconds) {
return new Promise((resolve, reject) => {
if (!seconds || seconds < 0) {
reject(new Error('seconds가 0보다 작음'));
}
setTimeout(resolve, seconds * 1000);
});
}
plank(5)
.then(() => console.log('플랭크 완료!'))
.catch(console.error)
.finally(() => console.log('운동 끝!'));
Promise Chaining#
function getRoutine(routine) {
return Promise.resolve(`${routine} => 🦵`)
}
function splitSquat(routine) {
return Promise.resolve(`${routine} => Split Squat😤`);
}
function Squat(workout) {
return Promise.resolve(`${workout} => Squat🤢`);
}
function legPress(workout) {
return Promise.resolve(`${workout} => Leg Press🤮`);
}
getRoutine('😘')
.catch(() => '🦵')
.then((workout) => splitSquat(workout))
.then((workout) => Squat(workout))
.then(legPress)
.then(console.log);
//.then((workout) => splitSquat(workout))
//== .then(splitSquat)
//.then(console.log);
//== .then((workout) => console.log)
Promise.all#
function doLunges() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Lunges😖');
}, 1000);
});
}
function doBulgarianSplitSquat() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Bulgarian Split Squat🤢');
}, 3000);
});
}
function doSquat() {
return Promise.reject(new Error("Can't Squat🤮🤮🤮🤮🤮"));
}
// 순차적으로 Promise를 실행
doLunges()
.then((lunges) =>
doBulgarianSplitSquat()
.then((BulgarianSplitSquat) => [lunges, BulgarianSplitSquat])
)
.then(console.log);
// Promise.all: 병렬로 한번에 모든 Promise를 실행
Promise.all([doLunges(), doBulgarianSplitSquat()])
.then((workouts) => console.log('all', workouts))
// Promise.race: Promise 중 가장 빨리 수행된 것이 처리(출력)됨
Promise.race([doLunges(), doBulgarianSplitSquat()])
.then((workouts) => console.log('race', workouts));
// all에서는 하나라도 실패하면 전체 실패, 에러 처리 불가
Promise.all([doLunges(), doBulgarianSplitSquat(), doSquat()])
.then((workouts) => console.log('all-error', workouts))
.catch(console.log);
// Promise.allSettled: 모든 Promise가 완료되면 결과를 반환, 에러 처리 가능
Promise.allSettled([doLunges(), doBulgarianSplitSquat(), doSquat()])
.then((workouts) => console.log('all-settle', workouts))
.catch(console.log);
async, await#
Promise를 리턴하는 함수
function doLunges() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Lunges😖');
}, 1000);
});
}
function doBulgarianSplitSquat() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Bulgarian Split Squat🤢');
}, 3000);
});
}
function doSquat() {
return new Promise.reject(new Error("Can't Squat🤮🤮🤮🤮🤮"));
}
then을 이용하여 콜백, 각 함수가 실행이 완료되길 기다리며 차례대로 실행됨
// 루틴 가져오기
function getRoutine(){
return doLunges()
.then((lunges) =>
doBulgarianSplitSquat()
.then((BulgarianSplitSquat) => [lunges, BulgarianSplitSquat])
);
}
getRoutine()
.then((routine) => console.log(routine));
async, await를 이용하여 위의 코드를 깔끔하게 표현
async가 있는 함수 안에서 await를 만나면 실행이 완료되길 기다렸다가 완료되면 다음 코드를 실행
// async 버전
async function getRoutineAsync(){
const lunges = await doLunges();
const bulgarianSplitSquat = await doBulgarianSplitSquat();
let squat;
try {
squat = await doSquat();
} catch (error) {
console.log(error);
squat = "Can't Squat🤮🤮🤮🤮🤮";
} finally {
return [lunges, bulgarianSplitSquat, squat];
}
}
getRoutineAsync()
.then((routine) => console.log(routine));
JSON#
직렬화와 역직렬화가 가능합니다. 함수의 경우 직렬화 시 포함되지 않습니다.
// Serializing (직렬화): 객체를 문자열로 변환
const json = JSON.stringify(ruff);
// Deserializing (역직렬화): 문자열 데이터를 자바스크립트 객체로 변환
const obj = JSON.parse(json);
fetch()#
fetch("API 경로 e.g. https://skitttles.me/api/getname")
.then((response) => response.json())
.then((data) => console.log(data.dataseries));
// json(): 받은 문자열을 javascript object로 변환, Promise로 리턴