JavaScript

Juyeon.it·2022년 5월 13일
0

정리 기준
1. 평소에 사용하면서도 애매하게 알고 있었던 것
2. 있다는 건 알고 있지만 사용하려고 하면 맨날 오류나서 결국 사용하지 못하는 개념들

How to use script tag

  1. head 태그 안에 그냥 script 넣기
    parsing HTML -> fetching JS, executing JS -> parsing HTML
  • JS 파일이 클 경우 나머지 HTML을 parsing하기 까지 오래 걸림
<head>
	<script src="main.js"></script>
</head>
  1. body 태그 맨 끝에 그냥 script 넣기
    parsing HTML(끝까지) -> fetching JS, executing JS
  • 웹사이트가 js파일에 많이 의존적이라면, 사용자가 의미있는 콘텐츠를 보기 위해 시간이 오래 걸림
<body>
    ...
	<script src="main.js" async></script>
</body>
  1. head 태그 안에 async로 script 넣기
    parsing HTML 하면서 동시에 fetching JS, fetching JS가 끝나면 HTML 받아오던거 멈추고 -> executing JS -> parsing HTML
  • 2번 방법 보다는 fetching JS가 병렬적으로 일어나기 때문에 fetching JS하는 시간을 절약할 수 있음
  • 하지만 HTML을 전부 받아오기 전에 js가 실행되기 때문에 js가 실행되는데 필요한 요소들이 아직 준비가 안되어 있을 수 있음
  • 여러 js파일을 모두 async로 받아올 경우, 다운로드가 완료된 js파일 순서에 따라 실행되기 때문에 js파일들 간의 의존적인 기능이 있다면 제대로 작동하지 않을 수 있음
<head>
	<script src="main.js" async></script>
</head>
  1. head 태그 안에 defer로 script 넣기
    parsing HTML 하면서 동시에 fetching JS, parsing HTML이 모두 끝난 후 -> executing JS
  • fetching JS와 parsing HTML이 병렬적으로 진행되어 시간도 아끼고, HTML이 모두 준비된 후에 js를 실행할 수 있음
<head>
	<script src="main.js" defer></script>
</head>

Class

Getter and Setter

class User {
  constructor(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
 
  get age() {
    // 위에 있는 this.age;는 메모리를 읽어오는 것이 아니라
    // 이 getter를 호출하게 됨
    return this._age;
  }
  
  // setter를 정의하는 순간 위에서 = age;를 호출할 때(값을 할당할 때),
  // 메모리에 값을 할당하는 게 아니라 이 setter를 호출하게 됨
  // 이 말은, setter 안에서 전달된 value를 age에 할당 할 때,
  // 메모리에 값을 할당하는게 아니라 setter를 호출하게 됨. 그러면 무한 반복
  // 따라서 getter와 setter에 쓰여지는 변수는 이름을 다르게 쓴다
  set age(value) {
    // 
    this._age = value;
  }
}

Public field and Private field

class Experiment {
  publicField = 2;
  #privateField = 0;
}

const experiment = new Experiment();
console.log(experiment.publicField); // 2
console.log(experiment.privateField); // undefined

Static properties and methods

class Article {
  // object에 상관 없이 class 자체에 연결되어 있음
  static publisher = 'Dream Coding';
  constructor(articleNumber) {
    this.articleNumber = articleNumber;
  }
  
  static printPublisher() {
    console.log(Article.publisher);
  }
}

const article1 = new Article(1);
console.log(article1.publisher); // undefined
console.log(Article.publisher); // Dream Coding
Article.printPublisher(); // Dream Coding

Object

for ... in vs. for ... of

// for (key in obj)
for (key in ellie) {
  console.log(key); // ellie 안에 있는 모든 키 출력
}

// for (value of iterable)
const array = [1, 2, 3];
for (value of array) {
  console.log(value);
}

Cloning

const user = { name: 'ellie', age: 20 }
const user2 = user;
// user는 레퍼런스가 들어있는 메모리를 가리키고 있고, 그 레퍼런스는 데이터를 가지고 있는 오브젝트를 가리킨다
// user2의 변수는 user안에 있는 레퍼런트의 값이 동일한 레퍼런스가 들어있음, 따라서 똑같은 오브젝트를 가르킴

user2.name = 'coder';
console.log(user); // { name: 'coder', age: 20 }

// 위가 아니라 진짜 복사를 하고 싶다면
// old way
const user3 = {};
for (key in user) {
  user3[key] = user[key];
}
// new way
const user4 = {};
Object.assign(user4, user); // Object.assign(target, source);
// 위 두줄을 한줄로 하면
const user4 = Object.assign({}, user);
// 여러 오브젝트를 줄 수 있음
const mixed = Object.assign({}, fruit1, fruit2);
// 동일한 키가 있다면 뒤에 있는 값이 계속 덮어 씌움

JSON

Object to JSON

// stringify converts a JavaScript value to a JavaScript Object Notation(JSON) string.
let json = JSON.stringify(true);
console.log(json); // true

json = JSON.stringify(['apple', 'banana']);
console.log(json); // ["apple", "banana"] -> "" 사용은 JSON의 규격 사항

const rabbit = {
  name: 'tori',
  color: 'white',
  size: null,
  birthDate: new Date(),
  jump: () => {
    console.log(`${this.name} can jump!`);
  }
};
json = JSON.stringify(rabbit);
console.log(json);
// {"name":"tori", "color":"white", "size":null,"birthDate":"2022-01-10어쩌구"}
// jump 함수는 제외됨, 데이터가 아니기 때문
// symbol같이 JS에만 있는 특별한 데이터도 포함되지 않음

json = JSON.stringify(rabbit, ["name", "color"]);
console.log(json); // {"name":"tori", "color":"white"}

json = JSON.stringify(rabbit, (key, value) => {
  console.log(`key: ${key}, value: ${value}`);
  return value;
  // 결과는 아래 이미지
});

json = JSON.stringify(rabbit, (key, value) => {
  console.log(`key: ${key}, value: ${value}`);
  return key === 'name' ? 'ellie': value;
});
console.log(json); // {"name":"ellie", "color":"white", "size":null,"birthDate":"2022-01-10어쩌구"}

JSON to Object

// parse converts a JavaScript Object Notation(JSON) string into an object.
json = JSON.stringify(rabbit);
const obj = JSON.parse(json);
console.log(obj);
// {name: "tori", color: "white", size: null, birthDate: "2022-01-10어쩌구"}
// stringify를 할 때 함수는 변환되지 않기 때문에 parse한 결과에서도 함수가 없다
rabbit.jump(); // tori can jump!
obj.jump(); // jump is not a function

console.log(rabbit.birthDate.getData()); // 10
// 값이 그냥 string이기 때문.. rabbit에 있던 birthDate은 데이트라는 오브젝트 였었다
console.log(obj.birthDate.getData()); // getDate is not a function

// 세밀하게 parse 하고 싶을 때
const obj = JSON.parse(json, (key, value) => {
  return key === 'birthDate' ? new Date(value) : value;
}
console.log(obj.birthDate.getDate); // 10

비동기 처리_Promise 사용 예시

class UserStorage {
  loginUser(id, password) {
	  return new Promise((resolve, reject) => {
			setTimeout(() => {
		    if (
		      (id === 'ellie' && password === 'dream') ||
		      (id === 'coder' && password === 'academy')
		    ) {
		      resolve(id);
		    } else {
		      reject(new Error('not found');
		    }
			}, 2000);
	  }
  });
    

  // 사용자의 데이터를 받아서 그 정보를 바탕으로 백엔드에 해당 사용자의 역할을 요청해서 받아오는 api
  getRoles(user) {
    return new Promise((resolve, reject) => {
			setTimeout(() => {
	      if (user === 'ellie') {
	        resolve({ name: 'ellie', role: 'admin' });
	      } else {
	        reject(new Error('no access');
	      };
		  }, 1000);
    }); 
  } 
}
  
const userStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');

userStorage
// 로그인을 하고
.loginUser(id, password)
// 로그인에 성공하면 그 유저를 이용해서 역할을 받아오고
.then(user => userStorage.getRoles)
// 그것에 성공하면 alert를 띄운다
.then(user => alert(
  `Hello ${user.name}, you have a ${user.role} role`
))
.catch(console.log);

비동기 처리_Async, await 사용 예시

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple() {
  // await을 쓰면 delay가 진행되는 3초동안 기다려줌
  await delay(3000);
  // throw 'error';
  return '🍎';
}

async function getBanana() {
  await delay(3000);
  return '🍌';
}
// getBanana를 promise로 써보면 아래와 같이 쓸 수 있다
// 하지만 위와 같이 동기적인 코드를 쓰는 것처럼 하면 쉽게 이해할 수 있음
function getBanana() {
  return delay(3000);
  .then(() => '🍌');
}

// 아래와 같이 하면 콜백 지옥 비슷해짐
function pickFruits() {
  return getApple()
  .then(apple => {
    return getBanana()
    .then(banana => `${apple} + ${banana}`)
  })
}

// 위 코드를 async를 써서 해보자
async function pickFruits() {
  // try, catch로 에러처리 해줄 수도 있음
  const apple = await getApple();
  const banana = await getBanana();
  return `${apple} + ${banana}`;
}
// 여기서 문제는 사과를 받는데 3초가 지나고 또 바나나를 받는데 3초가 지남
// apple과 banana 사이에는 서로 연관이 없기 때문에 서로를 기다릴 필요가 없다

// 따라서 아래와 같이 해볼 수 었음
// const를 만드는 순간 코드는 실행이 된다, 따라서 미리 만들어두고 await을 건다
// 병렬적으로 실행 가능, 하지만 코드가 정말 더러워짐
// 아래 promise api를 이용해봐
async function pickFruits() {
  const applePromise = getApple();
  const bananaPromise = getBanana();

  const apple = await applePromise();
  const banana = await bananaPromise();
  return `${apple} + ${banana}`;
}

pickFruits().then(console.log);

// Useful Promise APIs
function pickAllFruits() {
  // 프로미스 배열을 전달해주면 모든 프로미스들이 병렬적으로 받을 때까지 모아주는 아이
  return Promise.all([getApple(), getBanana()])
	// 이 경우 결과가 배열로 받아짐
  .then(fruits => fruits.join(' + ');
}
pickAllFruits().then(console.log); // 🍎 + 🍌

// 어떤 과일이던 먼저 따지는 과일 받아오기
function pickOnlyOne() {
  // 배열에 전달 된 프로미스 중에서 값을 가장 먼저 리턴하는 아이만 전달되어짐
  return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log);

Nullish Coalescing operator

leftExpr ?? rightExpr
- 왼쪽 코드가 null, undefined인 경우에만 오른쪽 코드가 실행된다
leftExpr || rightExpr
- 왼쪽 코드가 falsy인 경우에만 오른쪽 코드가 실행된다

// Bad
function printMessage(text) {
  let message = text;
  if (text == null || text == undefined) {
    message = 'Nothing to display';
  }
  console.log(message);
}

// Good
function printMessage(text) {
  // text가 있다면 그대로 쓰고 없으면 'Nothing to display'
  const message = text ?? 'Nothing to display';
  console.log(message);
}

printMessage('Hello'); // Hello
printMessage(undefined); // Nothing to display
printMessage(null); // Nothing to display

// 위 함수를 default 값을 준다면?
// undefined인 경우에만 default값이 할당 됨
function printMessage(text = 'Nothing to display') {
  console.log(text);
}

printMessage('Hello'); // Hello
printMessage(undefined); // Nothing to display
printMessage(null); // Null

Optional Chaining

const bob = {
  name: 'Julia',
  age: 20
};
const anna = {
  name: 'Julia',
  age: 20,
  job: {
    title: 'Software Engineer',
  }
};

// Bad
function displayJobTitle(person) {
  if (person.job && person.job.title) {
    console.log(person.job.title);
  }
}

// Good
function displayJobTitle(person) {
  // job이 있다면 그 job 안에 title이 있는지 아닌지 확인
  if (person.job?.title) {
    console.log(person.job.title);
  }
}

function displayJobTitle(person) {
  const title = person.job?.title ?? 'No Job Yet';
  console.log(title);
}

AJAX_요즘 사용 방식

fetch('https://codingapple1.github.io/price.json')
  .then((response) => {
    if (!response.ok) {
      throw new Error('Error 400 or Error 500')
    }
    return response.json()
  })
  .then((result) => {
    console.log(result);
  })
  .catch(() => {
    console.log('에러남');
  })

OR

async function fetchingData() {
  let response = await fetch('https://codingapple1.github.io/price.json');
  if (!response.ok) {
    throw new Error('Error 400 or Error 500');
  }
  let result = await response.json();
  console.log(result);
}
fetchingData().catch(() => {
  console.log('에러남');
})

참고자료

Youtube 자바스크립트 기초 강의 by 드림코딩

0개의 댓글