javascript ES6 대충 알아보기

jangdu·2023년 5월 20일
0

javascript

목록 보기
14/16

javascript ES6

  • 2015년 js문법에 큰 변화가있었는데, 그때 나온 문법이 ES6이다.
  • 구형 브라우저에서는 호환이 안되는데,
    babel같은거로 문법 변환해주면 문제 없이 사용할 수 있다.

const, let

js를 배울때 var부터 배우는데 const, let만 사용하자.

  1. 스코프
    var는 함수나 if문같은 블록 안에서 선언해도 그 밖에서 사용가능한데
    constlet은 그 안에서만 사용 가능함
    그래서 호이스팅같은 변수 중복같은 문제가 관리하기 편해짐

  2. const, let 차이점
    const는 한번 값을 할당하면 다른 값을 할당할 수 없다.
    그래서 이거 상수라고 함
    한번 선언할 때 값을 이미 넣어주면 다른 값 할당하려하면 에러남
    let은 안그렇다.

js에서는 보통 한번 초기화한 변수에 다른 값을 넣는 일이 생각보다 적어서,
기본적으로는 const쓰는데, 다른거 할당할 때 let쓰면됨
var는 걍 버리자


템플릿 문자열

es6문법에 새로운 문자열,
백틱을 사용해 감싸는 방식 추가


객체 리터럴

ES6에서 객체리터럴에 새로 기능이 추가됐다.

object객체에 동적으로 속성을 추가할 때

const node = function () {
  console.log("node");
};

const es = "ES";
const old = {
  js: function () {
    console.log("js");
  },
  node: node,
};

old[es + 6] = "oh";
old.node();	// node
old.js();	// js
console.log(old.es6);   // oh

위처럼 사용했던 코드를 아래처럼도 사용할 수 있다.

const node = function () {
  console.log("node");
};
const newObj = {
  // 1.
  js() {
    console.log("js");
  },
  // 2.
  node,
  // 3.
  [es + 6]: 'oh'
};
newObj.node()	// node
newObj.js();	// js
console.log(newObj.es6)	// oh

차이점을 찾아보면,
1. 콜론(:)이랑 function키워드를 빼도 됨
2. node: node처럼 변수명이 같으면 한번만 써도됨 (node: node === node)
3. 객체의 속성명은 동적 생성 가능

객체리터럴에 추가된거 사용해서 편의성 높이고
코드 양 줄이면서 효율적 코딩이 가능해졌다.


화살표 함수

여기 정리해둠
https://velog.io/@jangdu/javascript-Arrow-function


구조 분해 할당

이거 쓰면 객체랑 배열에서 속성이나 요소를 꺼내기 쉬워진다.

객체안에 들어있는 속성을 같은 이름으로 된 변수로 만들어 넣는 코드를 구현할 때,
이거쓰면 편하다.
이거 나오기전에는 이렇게씀

var 담배한갑 = {
  status: {
    담배이름: '말보루',
    남은담배: '20',
  },
  get담배: function() {
    this.status.count--;
    return this.status.count;
  }
};
var get담배 = 담배한갑.get담배;
var 남은담배 = 담배한갑.status.남은담배;

이렇게 썼던 코드를 다음처럼 쓸 수 있다.

var 담배한갑 = {
  status: {
    담배이름: '말보루',
    남은담배: '20',
  },
  get담배: function() {
    this.status.남은담배--;
    return this.status.남은담배;
  }
};

const { get담배, status: { 남은담배 } } = 담배한갑;

담배한갑이라는 객체안에 속성을 알아서 그 이름에 맞는 변수에 넣는다.
남은담배처럼 좀 안쪽에 있는 것도 알아서 찾아줌

배열도 가능하다.

const arr = ['a', {}, 1, true];
const [node, obj, , bool] = arr;

그 순서에 맞게 변수명을 자리잡아주면 그 순서에 따른 값이 대입된다.
3번째 요소에 변수명 안쓰고 빈칸으로 넘어가면 1은 무시하고 넣는다.

모듈시스템을 자주 사용하는 노드에서는 이 방식을 자주 사용한다.


클래스

클래스도 추가됐다. 물론 다른언어랑은 다르게 프로토타입 기반으로 동작한다.
프로토타입기반 문법을 보기 좋게 바꾼거라고 생각하자.

프로토타입 상속 예제 코드

// Human생성자 함수
var Human = function (type) {
  this.type = type || "human";
};

Human.isHuman = function (human) {
    return human instanceof Human;
}

Human.prototype.breathe = function() {
    alert('aaaa')
}

var Duho = function(type, firstName, lastName){
    Human.apply(this, arguments);
    this.firstName = firstName;
    this.lastName = lastName;
}
// Duho 생성자 함수가 Human 상속
Duho.prototype = Object.create(Human.prototype);
Duho.prototype.constructor = Duho;
Duho.prototype.sayName = function(){
    alert(this.firstName + ' ' + this.lastName);
}
var old = new Duho('human', 'duho', 'jang');
Human.isHuman(old); //  true

생성자함수 부터 다른 생성자가 상송 받으려면 길어지고 난해하다.

이를 ES6에선 Class문법으로 아래 코드처럼 상당히 편하게 사용이 가능하다.

class Human {
  constructor(type = "human") {
    this.type = type;
  }

  static isHuman(human) {
    return human instanceof Human;
  }

  breathe() {
    alert("aaaa");
  }
}

class Duho extends Human {
  constructor(type, firstName, lastName) {
    super(type);
    this.firstName = firstName;
    this.lastName = lastName;
  }

  sayName() {
    super.breathe();
    alert(`${this.firstName} ${this.lastName}`);
  }
}

전반적으로 클래스 안으로 들어가서 그룹처럼 됐고,
생성자함수는 constructor안으로 들어갔다.
클래스함수는 static키워드로 사용한다.
프로토타입 함수들도 class블록 안으로 들어가서 어떤 함수가 어떤 클래스의 것인지 확인이 쉽다.

상속도 extend키워드로 간단하다.
그런데 클래스로 문법이 바뀌어도 js는 프로토타입 기반으로 동작한다.


프로미스

js에서는 주로 비동기란거를 사용한다. 특히 이벤트 리스너 같은거 쓸때 콜백을 자주쓰는데,
ES6에서부터 jsapi들이 콜백대신 Promis로 구성되서 이제 콜백 지옥이런거 ㄱㅊ아졌다.
프로미스에는 다음과 같은 규칙이 있다.

promise객체생성

우선 프로미스 객체를 생성한다.

const condition = true;
const promise = new Promise((resolve, reject) => {
  if(condition){
    resolve('succes');
  } else {
    reject('fail');
  }
}
promise
  .then((message) => {
    console.log(message);	//성공할 때 실행
  })
  .catch((err) => {
    console.error(err);	//실패시 실행
  }
  .finally(() => {
    console.log('끝나고 무조건 실행');	// 다 끝나고 실행(then이든 catch든)
  });

new Promise로 프로미스를 생성하고,
그 안에 resolve, reject를 파라미터로 받는 콜백을 넣는다.

그러면 이 promise변수에 then, catch를 넣을 수 있다.
finally는 성공하든 실패하든 끝나면 무조건 실행된다.

resolve랑 reject에 넣어준 건 then이랑 catch에서 받아서 사용한다.

즉, promise는 실행을 하긴하는데 결과는 나중에 받는 객체다.
결과는 실행이 다 끝나고 then이나 catch를 통해서 받는다.
그 사이에서는 다른 코드가 들어갈 수 있다.

promise then에 then 이어 붙이기

then이나 catch에서도 다른 then이나 catch를 이어 붙이기도 가능
그러면 그 전의 then에서 리턴을 다음 then의 매개변수로 넘긴다.
프로미스를 리턴하면 프로미스가 끝나고 나머지가 실행된다.

예를 들면

promise.then((message) => {
    return new Promise((resolve, reject) => {
        resolve(message);
    })
}).then((message2) => {
    console.log(message2);
    return new Promise((resolve, reject) => {
        resolve(message2);
    })
}).then((message3) => {
    console.log(message3);
}).catch((err) => {
    console.error(err);
})

첫번째에서 messageresolve하면, 그 다음에서 message2로 받는다.
그리고 message2resolve한게 다음에서 message3으로 받는다.
(근데 then에서 new Promise를 리턴해야만 다음 then에서 받음)

이를 활용해서콜백을 promise로 변경하자

하지만, 항상 콜백을 promise로 바꾸지는 못한다.
메서드가 프로미스 방식을 지원해야 가능함 (new Promise가 함수 내에 구현되있어야함)

프로미스를 여러개 한번에 실행할때는 이렇게 사용하자.

Promise.all

promise를 여러번 한번에 실행할때도 있다.
콜백패턴의 함수면 연속으로 중첩해서 사용했었지만,
Promise.all은 간편하게 사용이 가능하다.

// Promise.resolve(): 즉시 resolve하는 프라미스 생성
// Promise.reject도 있음
const promise1 = Promise.resolve('1번째');
const promise2 = Promise.resolve('2번째');
Promise.all([promise1, promise2])
.then((result) => {
  console.log(result);
}).catch((err) => {
  console.log(err);
})

Promise가 여러개가 있으면, 이거쓰면 resolve를 하고나서 then으로 간다.
위에서 result에는 [1번째, 2번째]이렇게 들어있음

모든 Promise중에 하나만 reject되면 바로 catch로 가버림
어디서 reject됐는지는 모른다.

정확하게 어디서 reject됐는지 보려면
Promise.allSettled 이거 쓰면된다.

Promise.allSettled

이거 쓰면 결과가 all보다 더 많아짐
어디서 reject됐는지 status로 알 수 있다.

const promise1 = Promise.resolve('1번째');
// node16부터 reject된 Promise에는 catch안달면 에러남
const promise2 = Promise.reject('2번째').catch(() => {});
Promise.allSettled([promise1, promise2])
.then((result) => {
  console.log(result);
}).catch((err) => {
  console.log(err);
})

위에서는 Promise두번째에서 reject돼서 catch로 오는데,
여기서 결과값은 아래처럼 나옴

[ 
  {status: 'fulfilled', value: '1번째'},
  {status: 'rejected', reason: '2번째'},
]

이 결과 값을 통해서 어디서 reject됐는지 확인할 수 있다.
그래서 all보다는 allSettled를 더 권장한다.


async/await

노드7.6부터 지원되는데 알아두고 자주쓰자
노드에서는 비동기 많이 쓰니까 이거 자주 쓰면 좋음

async/await with promise

promise가 콜백을 많이 보완했는데 그래도 코드가 긴듯,
then catch가 계속 반복되니까 어쩔수없다.
근데 async/await이거 쓰면 프로미스 쓰고 더 깔끔해진다.

function findUser(Users) {
  Users.findOne({})
  	.then((user) => {
    	user.name = 'du';
    	return user.save();
  }).then((user) => {
    return Users.findOne({gender: 'm'});
  }).then((user) => {
    ...
  }).catch (err => {
    console.error(err);
  });
}
    

이거보면 콜백보다 깊이도 짧고 ㄱㅊ긴한데 코드 자체가 좀 길다.
async/await으로 쓰면, 깔쌈해진다.

async function findUser(Users) {
  try {
    let users = await Users.findOne({});
    user.name = 'du';
    user = await user.save();
    user = await User.findOne({gender: 'm'});
	...	
  } catch (err) {
    console.error(error) {
  }
}

함수 선언부를 일반 함수에서 async function으로 교체하고, promise앞에 await만 붙였다.

이제 이 함수에선 await붙인 Promise가 resolve될때 까지 기다리고 진행된다.

위 코드에서 try catchasync/await을 사용할 때 reject를 걸러주는 용도로 사용했다.
(reject가 발생하면 항상 걸러주는 작업 필요)

for문에서 async/await

node 10 버전부터는 for문을 사용할 때도 async/await쓰면 promise를 순서대로 실행할 수 있다.

const promise1 = Promise.resolve('1');
const promise2 = Promise.resolve('2');
(async () => {
  for await (promise of [promise1, promise2]) {
    console.log(promise);
  }
})();

for await of: promise배열을 순서대로 진행하는 문법
위 코드역시 reject가 발생 시에는 처리해줘야하는건 알아두고 넘어가자

async의 리턴은 무조건 Promise형태이다.
그래서 실행하고 then이나 다른 함수에서도 await을 붙어 이어나갈 수 있다.


Map/Set

ES2015부터 추가된 자료구조인데, 좀 많이쓰인다.
Map은 객체랑 유사하고, Set은 배열이랑 유사하다.

Map

Map을 생성하고

const m = new Map();

set이나 get을 이용해서 넣고 확인이 가능하다

  • set(key, value): Map에 속성을 추가
  • get(key): 속성의 값 조회
  • size: 속성의 갯수
  • key값으로 문자열 말고 객체 숫자 다 사용이 가능
m.set('a', 'b');
m.set(3, 'c');
const d = {};	// 객체도 들어간다.
m.set(d, 'e');

m.get(d);   // get(key)

m.size; // 속성 갯수

Map은 순서도 있어서 반복문도 사용이 가능하다
for, forEach 다 사용가능

for (const [k, v] of m) {
    console.log(k, v);  // 'a', 'b', 3, 'c', {}, 'e'
}

m.forEach((v, k) => {
    console.log(k, v)
})

m.has(d);
console.log(m.has(d))   // true

m.delete(d);    // 삭제
m.clear();      // 전부 삭제
  • delete(key): 삭제
  • clear(): 전부 제거
  • has(key): 속성이 존재하는지 참거짓

Set

set 선언

const s = new Set();

Set은 add(요소)를 이용해서 속성을 추가한다.
근데 배열과는 달리 중복을 허용하지 않는다.

s.add(false);
s.add(1);
s.add(1);	// 중복 무시

Set에서도 Map처럼 size, has(요소), for문 forEach 다 가능하다.
delete() clear()도 ㅇㅇ

기존에 배열에서 중복된 요소 없애고 싶을 때 Set을 사용하면 편하게 가능하다.

const arr [1, 2, 3, 4, 1, 2];

const s = new Set(arr);
const result = Array.from(s);	// s를 배열로
console.log(result); // 1, 2, 3, 4

위 코드처럼 사용하면 배열에서 중복제거가 편리하다.
배열을 쓰고싶은데 중복하고싶지 않을 때 Set사용하자.


Null 병합, 옵셔널 체이닝 (??, ?)

??(Nullish Coalescing), ?(Optional Chaining)은 ES2020에서 추가된 연산자이다.

?: Null 병합 (Nullish Coalescing)

널 병항 연산자는 보통 ||의 대용으로 쓰고,
0, '', false, NaN, null, undefined 중에서 null이랑 undefined만 따로 구분해서 사용하면 된다.

const a = 0;
const b = a || 3;
console.log(b);	// 3

const c = a ?? 3;
console.log(c);	// 0

const a = null;
const d = a ?? 3;
console.log(d);	// 3

const a = undefined;
const d = a ?? 3;
console.log(d);	// 3

||이거는 앞의 값이 false이면 뒤로 넘어가는데,
??는 앞의 값이 Null이나 undefined일 때만 넘어간다.

옵셔널 체이닝

옵셔널 체이닝은 null이나 undefined의 속성을 조회할 때 에러가 나오는것을 막는다.

const a = {}
a.b;	// 객체라서 상관없음

const c = null;
c?.d

위 코드에서 c안에 값이 null이나 undefinde라면 접근할 때 오류가 나기 마련인데,
?를 사용하면 접근할 때 에러가 나오는것을 방지한다.

js를 만질때 TypeErr가 많은데, null에러 발생 빈도를 확실히 낮춘다.

profile
대충적음 전부 나만 볼래

0개의 댓글