Skip to content

[NOTE] Javascript ES6 독학 내용 필기

HYUNSANG HAN edited this page Sep 2, 2019 · 2 revisions

Javascript ES6

참고사항


ES6란?

ES6 === ECMA2015

let과 const(var와 다른 점)

둘 다 var와 다르게 block level scope. let은 가변적, const는 불변적. (하지만 const도 아예 불변이라고 볼 수는 없음!)
이제는 var를 쓰지 않음

Closure

내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가리킴.
초보개발자들은 종종 클로저 상황 때문에, 의도하지 않았던 실수를 하기도 하는데 let을 쓰면 이런 상황을 겪지 않을 수 있음

Immutable array

const list = ["apple", "orange", "watermelon"];
list.push("banana");

하면 기존 list에 banana가 아예 들어가버림.(const임에도 불구하고!) 반면에 concat을 이용하여 아래와 같이 작성하면 아예 새로 복사를 떠서 넣으니까 원본 훼손 없음

const list = ["apple", "orange", "watermelon"];
const listNew = [].concat(list, "banana")

특정 문자열 포함 여부 확인하기

(가정: str이라는 문자열을 예를 들어 "Hello, Hyunsang"이라고 선언 및 초기화해뒀다고 치자)

Hello로 시작하는 문자열인지 확인

str.startsWith("Hello")

Hyunsang으로 끝나는 문자열인지 확인

str.endsWith("Hyunsang")

H를 포함한 문자열인지 확인

str.includes("H")
  • 참고로, 모두 boolean을 반환함

for 반복문

  • for
  • forEach
  • for in (권장하지 않음)
  • for of (forEach와 비슷함 사실)
    • for of는 str에 사용할 경우, 한글자씩 뜯어줌

Spread operator

스프레드 연산자 혹은 펼침 연산자. 별 게 아니라, 말 그대로 그냥 펼쳐주는 것.

let pre = ["apple", "orange", 100];
let newData = […pre];

위 두 가지를 각각을 콘솔찍어보면 같게 찍힘

하지만 === 관계는 아님!! 즉, immutable array라는 뜻(concat과 마찬가지)

Spread operator는 심지어 배열 중간에 끼워넣을수도 있음

함수 매개변수에 여러개 할당할 때도 많이 쓴다. 아래의 예시를 보자.

function sum(a, b, c) {
   return a + b + c;
}
let pre = [100, 200, 300];
console.log(sum(…pre));

결과: 600

from

가짜배열(node list 등)도 진짜배열로 만들어주는 것

myNewArray = Array.from(myArray) //여기서 myArray는 가짜배열이라 가정

map

배열요소 하나하나 꺼내서 뭔가를 하려할 때 씀

let newData = prevData.map(value => {value + "!"});

위와 같이 하면 모든 요소의 값에 !가 붙어서 나옴

[예제 1] filter, include, from 을 사용해 e가 포함된 배열을 만들어 반환하기 (내가 작성한 답안 보기!)
function print() {
  /*filter, include, from 을 사용해 e가 포함된 배열을 만들어 반환하기*/  
  const list = document.querySelectorAll("li")
  let array = Array.from(list);
  let arrayNew = array.filter(value => value.innerText.includes("e"));
  arrayNew = arrayNew.map(value => value.innerText)
    console.log(arrayNew)
}
print();

Destructuring Array

let data = ["a", "b", "c", "d"]
let [wow,,now] = data;

콘솔 찍어보면? wow는 a, now는 c로 찍힘

Destructuring Object

내가 이미 React에서 자주 써왔던 비구조화할당임

Destructuring 활용 - 파싱

이미 사용해왔던 내용들은 필기 생략..
그 외 아래와 같은 활용도 가능함

function getNewsList([,{newslist}]) {
  console.log(newslist);
}

Destructuring 활용 - 이벤트객체 전달

이벤트객체 그대로 받아오지 않아도, Destructuring을 활용하면 이벤트객체 내의 target만 받아올 수도 있게 됨

document.querySelector("div").addEventListner("click", function({target}) {
  console.log(target.innerText)
});

Set

이미 우리가 아는 그 set과 같음. 중복을 불허하는 것.

let mySet = new Set();

위와 같이 인스턴스 만드는 형태로 선언해야함.

mySet.has("hi");

위와 같이 쓰면, hi가 있는지 boolean을 반환

mySet.delete("hi");

위와 같이 쓰면, hi를 삭제

mySet.add("hi");

위와 같이 쓰면, hi를 추가

[예제 2] 로또 번호 6가지 생성기 만들기 (내가 작성한 답안 보기!)
const SETTING = {
  name : "LUCKY LOTTO",
  count : 6,
  maxNumber : 45
};

const {count, maxNumber} = SETTING;
const lotto = new Set();

function getRandomNumbers(maxNum) {
  while(lotto.size < count) {
    const randNum = Math.floor(Math.random() * (maxNum - 1)) + 1;
    if (!lotto.has(randNum)) {
      lotto.add(randNum);
    }
  }  
}

getRandomNumbers(maxNumber);
lotto.forEach(n => {console.log(n)});

WeakSet

set이랑 비슷한데, 다만, 레퍼런스가 있는 객체만 저장 가능하다. (가비지컬렉션 대상이 아닌 경우만)

Map, WeakMap

Array용이 set이라고 한다면, object(key-value형태)용이 map이라고 볼 수 있음

Template처리

JSON으로 받아온 데이터를 이용해서 DOM의 텍스트를 조작(추가)하고 싶을 때 아래와 같은 문법으로 손쉽게 처리 가능

const template = `<div>환영합니다 ${data[0].name}님</div>`
  • [참고] `은 backtick이라고 부른다

Tagged Template literals

위와 같이 간단한 케이스면 상관없지만, 그렇지 않은 케이스에서는 function을 이용하는 방법도 있음. 참고로 아래 예시에서는 items가 없는 경우가 있으므로 이에 대한 예외를 처리해주기 위해 function을 활용해야만 했다.

const data = [
  {
    name : 'coffee-bean',
    order : true,
    items : ['americano', 'milk', 'green-tea']
  },
  {
    name : 'starbucks',
    order : false
  }
]

function fn(tags, name, items) {
  if(typeof items === "undefined") {
    items = "주문 가능한 상품이 없습니다";
  }
  return (tags[0] + name + tags[1] + items + tags[2]);
}

data.forEach((v) => {
  let template = fn`<div>welcome ${v.name}!!</div><h2>주문가능항목</h2><div>${v.items}</div>`;
  document.querySelector("#message").innerHTML += template;
});

Arrow function

우리가 늘상 쓰는 그 화살표함수.
화살표함수를 쓰면 this context를 유지하므로, .bind(this)를 생략해도 된다.

// NOT arrow function

const myObj = {
  runTimeout() {
    setTimeout(function() {
      this.printData();
    }.bind(this), 200); // 여기서, bind(this)를 생략하면 this === window이다. 따라서 this.printData();를 찾지 못해 에러가 난다.
  },
  printData() {
    console.log("Hi Hyunsang!");
  }
}

myObj.runTimeout();
// arrow function

const myObj = {
  runTimeout() {
    setTimeout(() => {
      this.printData();
    }, 200); // bind로 감싸주지 않아도 이 함수를 감싼 객체를 this로 인식하게 됨. 따라서 의도대로 this.printData();를 찾게 된다.
  },
  printData() {
     console.log("Hi Hyunsang!");
  }
}

myObj.runTimeout();

function의 default parameter

아래 두가지 방법으로 함수 파라미터의 기본값 설정이 가능함

function box(value, size) {
  size = size || 1;
  return value * size;
}
console.log(sum(3, 10)); // 결과: 30
console.log(sum(3)); // 결과: 3
function box(value, size = 1) {
  return value * size;
}
console.log(sum(3, 10)); // 결과: 30
console.log(sum(3)); // 결과: 3

Rest parameter

가변인자인 arguments는 본래 가짜배열로 들어오게 되기 때문에 배열의 유용한 기능들을 쓰기 위해 진짜배열로 바꿔줘야 함.
Spread operator와 좀 비슷하다고 볼 수 있는데, 매개변수에 …이 들어가서 배열로 받으면 Rest parameter라고 보면 됨.

function checkNum() { // NOT rest parameter
  const argArray = Array.prototype.slice.call(arguments); //가짜배열인 arguments를 진짜배열인 argArray로 변환해주는 과정
  const result = argArray.every((v) => typeof v === "number")
  console.log(result);
}
console.log(checkNum(10,2,3,4)); // true
console.log(checkNum(10,2,3,4,"55")); // false
function checkNum(…argArray) { // rest parameter
  const result = argArray.every((v) => typeof v === "number")
  console.log(result);
}
console.log(checkNum(10,2,3,4)); // true
console.log(checkNum(10,2,3,4,"55")); // false
  • 참고로 위 예시에서 every라는 것은, 모든 케이스가 만장일치로 true일 때 true를 반환한다.

ES6의 class

원래 javascript에는 클래스가 없음. 오직 함수뿐인데, ES6에서는 class라는 '키워드'가 생겼다.
하지만 이는 모습만 class지, 사실은 내부적으로는 function 형태로 돌아가 그 실체는 함수라고 볼 수 있다.(type을 찍어보면 함수라고 나옴) 가짜 class임에도, 가독성 면에서 장점을 지니므로 나름대로 쓸 만은 함.

참고로, 아래 두 예시는 완전히 같은 의미이다.

// 함수 형태 예시
function Health(name, healthTime) {
  this.name = name;
  this.healthTime = healthTime;
}
  
Health.prototype.showHealth = function() {
  console.log("안녕하세요" + this.name);
} // Health 인스턴스의 __proto__(즉, prototype객체)에 showHealth 메서드를 넣겠다는 뜻임.

const myHealth = new Health("Hyunsang");
myHealth.showHealth(); // 안녕하세요 Hyunsang
// 클래스 형태 예시
class Health {
  constructor(name, healthTime) {
    this.name = name;
    this.healthTime = healthTime;
  }
  showHealth() {
    console.log("안녕하세요" + this.name);
  } // 이렇게만 써줘도 알아서 __proto__(즉, prototype객체)에 들어감!
}

const myHealth = new Health("Hyunsang");
myHealth.showHealth(); // 안녕하세요 Hyunsang

Object assign으로 JS객체 만들기

const healthObj = {
  showHealth : function() {
    console.log("오늘 운동시간 : " + this.healthTime);
  }
}

const myHealth = Object.create(healthObj);

myHealth.name = "Hyunsang";
myHealth.healthTime = "11:20";

console.log(myHealth); // myHealth 바로 안에는 name, healthTime이 있고, __proto__ 안에 showHealth가 있음

이는 아래와 같이 Object assign으로 좀더 짜임새 있게 구현 가능하다.

const healthObj = {
  showHealth : function() {
    console.log("오늘 운동시간 : " + this.healthTime);
  }
}

const myHealth =  Object.assign(Object.create(healthObj), {
  name : "Hyunsang",
  healthTime : "11:20"
}); // 참고로 object create는 ES5 때 이미 생긴 것임

console.log(myHealth); // myHealth 바로 안에는 name, healthTime이 있고, __proto__ 안에 showHealth가 있음

Object assign은 사실 immutable하게 object를 만드는 방법이기도 하다.

const prevObj = {
  name : "Hyunsang",
  healthTime : "11:20"
};

const myHealth = Object.assign({}, prevObj, {
  "healthTime" : "12:30"
});

console.log(myHealth); // healthTime property가 11:20이 아닌, 12:30로 찍히게 됨
  • Object assign 시 3가지 인자를 넣게 되는데, 첫번째 인자는 prototype에 들어갈 메서드고, 두번째 인자는 기존 객체, 세번째 인자는 변경할 객체이다. 이는 마치 HTTP의 patch메서드와 비슷한 느낌으로 볼 수 있어서 기존 객체와 비교 후 다른 부분만 갈아끼운다. 참고로, immutable이기 때문에 기존 객체는 보존된다. 심지어 세번째 인자에 빈 객체를 넣어도, 새로 객체를 만들어내기 때문에 값이 똑같을지언정 서로 다른 객체가 된다.

Object setPrototypeOf

setPrototypeOf로 객체를 만들기 위한 문법은 이렇다.

Object.setPrototypeOf(프로토타입설정할대상객체, 프로토타입안에들어가고싶은객체);

Object setPrototypeOf 로 객체간 prototype chain을 생성할 수 있어, 다른 객체의 메서드를 재사용할 수 있게 하는 데에 효율적이다.

아래 예시를 보면, childObj의 __proto__에 있는 healthChildObj의 __proto__에 있는 healthObj의 setHealth와 showHealth를 사용하는 모습을 볼 수 있음.(순차적으로 내려가면서 setHealth 및 showHealth 메서드가 있는지를 찾게 되고, 못찾으면 더 아래 __proto__로 타고 내려가게 되는 것임)

//parent
const healthObj = {
  showHealth : function() {
    console.log("오늘 운동시간 : " + this.healthTime);
},
  setHealth : function(newTime) {
    this.healthTime = newTime;
  }
}

//child
const healthChildObj = {
  getAge : function() {
    return this.age;
  }
}
Object.setPrototypeOf(healthChildObj, healthObj);

const childObj = Object.setPrototypeOf({
  age : 22
}, healthChildObj);

childObj.setHealth("11:55");
childObj.showHealth(); // 오늘 운동시간 : 11:55

module(export & import)

js에서 module의 export와 import는 아직 실험적인 단계로 보여짐. 아직 일부 브라우저에서는 호환이 안되고 있어서, Webpack 기반의 build 환경과 Babel과 같은 transpiling이 필요하기 때문.

// myLogger.js

export default function makeLog(data) {
  console.log(data);
}

export const getCurrentTime = () => {
  return Date.now();
}
// app.js

import makeLog, { getCurrentTime } from './myLogger' // .js확장자는 웹팩을 설정하면 이렇게 생략이 가능함

makeLog(`현재 시각은 ${getCurrentTime()}`);
  • export를 쓰는 경우, 객체가 export되는 것이므로 import할 때 마치 Destructuring 하듯이 중괄호를 붙여 받아줘야 함
  • 반면에 export default를 쓰면 중괄호 없이 import 해올 수 있음
  • export default 는 const나 let과 같은 줄 내에 이어서 작성하지 못한다고 가이드가 되어있음. 평소에 React 쓸 때에도 이를 따로 맨 마지막쯤에 이를 써왔는데 이 이유 때문임.

Proxy와 Interception

Proxy는 객체를 변경하거나(set) 얻으려거나(get) 등을 할 때 중간에 부수적인 일들을 할 수 있게 해주는 껍데기임.
아직 프록시에 대한 활용법이 많이 알려지진 않았는데, log를 남기거나, 다른 데이터에도 영향을 미치게 할 때 쓰면 유용할 수 있음.

문법은 이렇다.

const 프록시객체 = new Proxy(원래있던 객체, {조작할 내용});

아래는 실제로 활용한 예다. Interception 후 getter, setter가 작동하게 되어있고, 특히 setter에서는 몇번 set되었는지 횟수를 기록하게끔 구현되었다.

const myObj = {name : 'Hyunsang', countChange : 0 };
const myProxy = new Proxy(myObj, {
  get : function(target, property, receiver) {
    console.log('성공적인 get value');
  },
  set : function(target, property, value) {
    console.log('성공적인 set value');
    target['countChange']++; // 변화가 일어날 때마다 횟수를 1씩 증가시켜줌
    target[property] = value;
  }
});

이 상황에서 getter, setter를 작동시키면?

myProxy.name // 성공적인 get value \n "Hyunsang"
myProxy.name = "HS" // 성공적인 set value  \n "HS" 

ES6와 관련된 기타 등등

Webpack

Webpack의 기능은 대략 이렇다.

  • loader : 임포트 해서 쉽게 쓸 수 있게 해주는 것
  • plugin : 다양한 코드를(공백 줄이기, 파일에 코드를 추가로 넣어준다거나, 코드 검사한다거나) 묶여진 번들파일을 대상으로 작업을 수행할 수 있게 해주는 것

Babel

ES6를 모든 브라우저에서 지원하는 건 아니다. 고로 Webpack에서 Babel을 load 해서 ES6를 ES5 코드로 transpiling해주는 것이 필요하다.

실습 프로젝트(찜목록 만들기)

이는 별도의 Wiki Page를 만들어 작성할 예정