<script>
태그를 <head>
에 작성할 경우 브라우저가 한 줄씩 파싱하다가, 해당 태그를 만나면 block되고 필요한 JS 파일을 다운 받아서 실행한 다음, 다시 파싱으로 넘어감
<body>
의 가장 끝 부분에 추가할 경우, HTML 파싱이 끝난 다음 JS 파일을 fetching하고 실행하게 됨
<head>
에 작성하되 async
속성을 이용
→ HTML 파싱을 하다가 해당 <script>
태그를 만나면 병렬적으로 fetch
→ JS 파일 다운로드가 끝나면 HTML 파싱을 잠깐 멈추고 실행
(JS 파일을 실행하는 동안 HTML 파싱이 block됨 & 여러 스크립트가 있을 경우, 순서 상관 없이 먼저 다운로드 되는 파일이 먼저 실행됨 (순서 보장 X))
<head>
<script async src="main.js"></script>
</head>
<head>
에 작성하되 defer
속성을 이용
→ HTML 파싱을 하다가 해당 <script>
태그를 만나면 다운로드 명령만 내리고 파싱 끝까지 실행. 파싱이 다 끝난 다음에 JS 파일 실행
'use strict';
favor immutable data type always
: 웬만하면 변경 불가능한 데이터 타입을 사용해라let nothing = null;
let x;
const symbol1 = Symbol('id');
const symbol2 = Symbol('id');
console.log(symbol1 === symbol2); // false
const symbol1 = Symbol.for('id');
const symbol2 = Symbol.for('id');
console.log(symbol1 === symbol2); // true
console.log(`value: ${symbol1.description}`); // value: id
// .description을 이용해서 출력
const yj = { name: 'yj', age: 20 };
typescript
)let text = 'hello';
console.log(`value: ${text}, type: ${typeof text}`); // value: hello, type: string
text = 1;
console.log(`value: ${text}, type: ${typeof text}`); // value: 1, type: number
text = '7' + 5;
console.log(`value: ${text}, type: ${typeof text}`); // value: 75, type: string
text = '8' / '2';
console.log(`value: ${text}, type: ${typeof text}`); // value: 4, type: number
function showMessage(message, from='unknown') {
console.log(`${message} by ${from}`);
}
showMessage('Hiii');
const print = function() { // 익명 함수 (function expression)
console.log('print');
}
// 여기서는 printYes, printNo가 콜백 함수
function randomQuiz(answer, printYes, printNo) {
if (answer === 'love you') {
printYes();
} else {
printNo();
}
}
const printYes = function() {
console.log('yes');
}
디버깅을 편리하게 하거나, 재귀 호출을 하기 위해 사용
const printNo = function print() {
console.log('yes');
print();
}
always anonymous
const simplePrint = () => console.log('print');
IIFE: Immediately Invoked Function Expression
(function hello() {
console.log('IIFE');
})()
붕어빵을 만들 수 있는 틀, 템플릿 같은 것
- declare once
- no data in
class Person {
// constructor
constructor(name, age) {
// 속성 (field)
this.name = name;
this.age = age;
}
// 행동 (method)
speak() {
console.log(`${this.name} hello!`);
}
}
const yj = new Person('yujin', 20);
console.log(yj.name);
yj.speak();
Getters & Setters
오브젝트에 설정한 프로퍼티에 조작을 하고 싶을 경우 이를 처리하기 위해 setter 사용
class User {
constructor(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
// getter를 정의하면 this.age는 메모리에서 읽어오는 게 아니라 getter 호출
// setter를 정의하면 값 할당 시 바로 메모리에 할당하는 게 아니라 setter 호출
// -> 따라서 콜스택 에러가 날 수 있으므로 getter/setter 안에서는 변수명을 조금 다르게 해줌
this.age = age;
}
get age() {
return this._age;
}
set age(value) {
this._age = value < 0 ? 0 : value;
}
}
const user1 = new User('Yj', 'Won', -1);
console.log(user1.age);
상속
class Shape {
constructor(width, height, color) {
this.width = width;
this.height = height;
this.color = color;
}
draw() {
console.log(`drawing ${this.color} color of`);
}
getArea() {
return this.width * this.height;
}
}
// extends를 통해 상속하면 부모 클래스의 필드, 메서드 사용 가능
class Rectangle extends Shape {}
class Triangle extends Shape {
// 메서드 오버라이딩 가능
draw() {
// 오버라이딩 시 부모 클래스에 있는 같은 이름의 메서드는 호출되지 않음
// 호출하고 싶을 경우 super 키워드 사용
super.draw();
console.log('^_^');
}
getArea() {
return (this.width * this.height) / 2;
}
}
function Person(name, age) {
// this = {};
this.name = name;
this.age = age;
// return this;
}
const user = { name: 'wyj', age: 20};
const user2 = user;
user2.name = 'hi'; // 같은 곳을 가리키기 때문에 user의 name이 바뀜
console.log(user);
// 복사해서 같은 곳을 가리키지 않는 새로운 오브젝트를 사용하고 싶다면 Object.assign() 사용
const user3 = Object.assign({}, user);
user3.name = 'assign!';
console.log(user);
console.log(user3);
AJAX
fetch() API
→ XML을 사용하면 파일의 크기도 커질뿐 아니라 가독성도 좋지 않음
→ 요즘은 JSON을 많이 사용
stringify()
const rabbit = {
name: 'tori',
color: 'white',
birthDate: new Date(),
// 함수나 Symbol처럼 JS에만 있는 데이터는 변환되지 않음
jump: () => {
console.log(`${name} can jump!`);
}
}
// jump를 제외하고 문자열로 변환됨
const json = JSON.stringify(rabbit);
console.log(json);
parse()
stringify()
, parse()
메서드의 reviver 함수 사용법// 좀 더 세밀하게 통제하고 싶을 때 사용
let json = JSON.stringify(rabbit, ['name', 'color']);
console.log(json);
json = JSON.stringify(rabbit, (key, value) => {
return key === 'name' ? 'yj' : value;
});
console.log(json);
새 프로미스가 만들어질 때, executor 함수가 자동으로 바로 실행됨
const promise = new Promise((resolve, reject) => {
// doing some heavy work (네트워크 통신, 파일 읽기 등)
console.log('doing something...');
setTimeout(() => {
resolve('yj');
// reject(new Error('no network'));
}, 2000);
});
then, catch, finally를 이용해서 값을 받아올 수 있음
promise
.then(value => {
// resolve 콜백함수를 통해 전달한 값이 value에 전달됨
console.log(value);
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log('finally');
})
.then
, .catch
를 chaining하면 코드가 복잡해질 수 있음 이 때 async&await를 사용하면 보통 코드를 작성하듯이 작성하여 동기적으로 동작하게 할 수 있음async
를 함수 앞에 쓰면 자동으로 리턴되는 값을 Promise로 만들어줌async function fetchUser() {
return 'yj';
}
const user = fetchUser();
user.then(console.log);
Promise도 여러 번 중첩하게 되면 콜백 지옥과 비슷한 문제 발생 (chaining으로 인한 가독성 저하 및 유지보수 어려움)
→ async await를 사용하면 기존에 동기적으로 코드를 작성하듯이 할 수 있고 가독성도 좋아짐
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple() {
await delay(1000);
return 'apple!';
}
async function getBanana() {
await delay(1000);
return 'banana!';
}
// Promise로 작성할 경우
// function pickFruits() {
// return getApple()
// .then(apple => {
// return getBanana()
// .then(banana => `${apple} + ${banana}`);
// });
// }
// async await
async function pickFruits() {
try {
// 단, 순차적으로 실행되어 1초 + 1초 -> 총 2초 소요
// 서로 연관된 것이 없는데도 순차적으로 실행되므로 비효율적
// const apple = await getApple();
// const banana = await getBanana();
// 병렬적으로 수행하는 법 (코드 더러운 ver)
const applePromise = getApple();
const bananaPromise = getBanana();
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
} catch(err) {
console.log(err);
}
}
pickFruits().then(console.log);
async function pickAllFruits() {
// all 메서드 사용 시 비동기적으로 실행 후 응답 array 반환
return Promise.all([getApple(), getBanana()])
.then(fruits => fruits.join(' + '));
}
pickAllFruits().then(console.log);