자바스크립트 ES6에서 ES12까지 한번에 살펴보기

안녕하세요?

오늘은 자바스크립트 역사에 대해 알아보려고 합니다.

자바스크립트는 Brendan Eich님께서 단 10일 만에 만든 언어인데요.

웹 개발에 있어 상당한 영향을 끼친 것도 사실이지만 자바스크립트 언어의 특성상 말도 안 되는 이상함으로 인해 개선할 게 상당히 많은데요.

자바스크립트의 이상함을 나타내는 다음 코드를 보시면 아마 다른 언어 개발자분들은 기절하실 건데요.

그래서 자바스크립트 좀 더 멀쩡하게 작동하게끔 생긴 게 바로 ECMAScript 규격입니다.

그럼 ES6부터 ES12까지 어떤 게 자바스크립트에 추가됐는지 한번 살펴보겠습니다.


ES6 (ES2015)

  1. Class

자바스크립트는 프로토타입 체인을 이용한 언어인데요,

사실 프로토타입 체인은 객체지향(OO) 언어와 비슷한 특징이 있지만 구현이 상당히 어려웠습니다.

그래서 ES6에서 드디어 class가 나왔습니다.

class Animal {
   constructor(name, color) {
     this.name = name;
     this.color = color;
   }
   // This is a property on the prototype chain
   toString() {
     console.log('name:' + this.name + ', color:' + this.color);

   }
 }

var animal = new Animal('myDog', 'yellow'); // instantiate
animal.toString(); // name: myDog, color: yellow

console.log(animal.hasOwnProperty('name')); //true
console.log(animal.hasOwnProperty('toString')); // false
console.log(animal.__proto__.hasOwnProperty('toString')); // true

class Cat extends Animal {
 constructor(action) {
   // The subclass must call the super function in the constructor, otherwise an error will be reported when new comes out
   // If the constructor was not written originally, the default constructor with super will be automatically generated
   super('cat','white');
   this.action = action;
 }
 toString() {
   console.log(super.toString());
 }
}

var cat = new Cat('catch')
cat.toString();

console.log(cat instanceof Cat); // true
console.log(cat instanceof Animal); // true

2. Module

각 모듈은 각각의 네임스페이스가 있어 충돌을 피할 수 있습니다.

import와 export를 쓰면 되죠.

기본적으로 .js 파일 자체가 하나의 모듈로 취급됩니다.

3. Arrow function

애로우 함수입니다. 다들 아실 겁니다.

const add = (a, b) => { return a + b};

const res = add(1, 2); // 3

// If the syntax is simple, `{}` and `return` can be omitted. It will look neater
const minus = (a, b) => a - b;
const res1 = minus(3, 1); // 2

4. Function parameter default value

함수에 파라미터를 전달하지 않고 호출하면 함수 선언에 있는 기본값이 적용됩니다.

function example(height = 50, width = 40) {
     const newH = height * 10;
     const newW = width * 10;
     return newH + newW;
}

example(); // 900 (50*10 + 40*10)

5. Template literal

예전에는 긴 문자열을 취급할 때 + 를 이용해서 합쳤는데요. 이제는 템플릿 리터럴을 이용해서 쉽게 쓸 수 있습니다.

const firstName = 'Ken';
const lastName = 'Huang';
// not use template literal
const name = 'Hello, My name is' + firstName + ', ' + lastName;
// use template literal
const nameWithLiteralString = `Hello, My name is ${firstName}, ${lastName}`;

6. Destructuring assignment

React에서 많이 쓰이는 Destructuring(디스트럭쳐링)입니다. 객체나 배열의 항목을 뽑아 쓸 때 아주 편합니다.

const arr = [1, 2, 3, 4, 5];
const [one, two, three] = arr;
console.log(one); // 1
console.log(two); // 2
console.log(three); // 3

// To skip certain values
const [first,,,,last] = arr;
console.log(first); // 1
console.log(last); // 5

// Objects can also be destructurized and assigned
const student = {
    name: 'Ken Huang',
    age: 38,
    city: 'Taipei'
};
const {name, age, city} = student;
console.log(name); // "Ken Huang"
console.log(age); // "38"
console.log(city); // "Taipei"

7. Spread operator

스프레드 연산자입니다.

const stuendts = ['Angel', 'Ryan'];
const people = ['Sara', ...stuendts, 'Kelly', 'Eason'];
conslog.log(people); // ["Sara", "Angel", "Ryan", "Kelly", "Eason"]

8. Object property shorthand

객체의 항목과 그에 대응하는 변수 이름이 같으면 생략할 수 있는 기능입니다.

const name = 'Angel', age = 18, city = 'ChangHwa';

// Before ES6, we must write like this
const customer = {
    name: name,
    age: age,
    city: city
} // // {name: 'Angel', age: 18, city: 'ChangHwa'}

// After ES6, we can do it
const newCustomer = {
    name,
    age,
    city
} // {name: '小明Angel, age: 18, city: 'ChangHwa'}

9. Promise

자바스크립트 콜백 지옥을 해결할 수 있는 Promise가 나왔습니다.

Promise를 쓰면 콜백을 좀 더 쉽게 쓸 수 있습니다.

const waitSecond = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000);
});
waitSecond.then( () => {
    console.log('hello World after 1 second.');
    // output this line after 1 second
    return waitSecond;
}).then( () => {
    console.log('Hell World after 2 sceond.');
    // output this line after 2second
})

10. let, const to replace var

드디어 var를 대체하는 let과 const가 나왔습니다.

초창기 var는 글로벌 스코프였는데 이제 let이 나오면서 블록단위 스코프가 가능해졌습니다.

console.log(a); // undefined
var a = 10;
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 10;


# ES7 (ES2016)

  1. Array.prototype.includes()

배열에서 특정 값을 가지고 있으면 true를 리턴하고 그렇지 않으면 false를 리턴합니다.

const arr = [1, 2, 3, 4, 5];
// Check if there is the number 3 in the array
arr.include(3); // true

if (arr.include(3)) { ... }
// ... Equivalent to the previous writing of indexOf
arr.indexOf(3); // 2 (return its array position)
// If you want to write it in the if, you must add `> -1`, which is not as clear as the include in ES7 in terms of semantics
if (arr.indexOf(3) > -1) { ... }

2. Exponentiation Operator

console.log(2**10); // 1024
// equal to
console.log(Math.pow(2, 10)); // 1024


# ES8 (ES2017)

  1. async, await

async, await가 나오면서 promise를 좀 더 깔끔하게 쓸 수 있게 되었습니다.

async test() {
    try {
        const result = await otherAsyncFunction();
        console.log(result); // output result
    } catch(e) {
        console.log(e); // Can catch errors if otherAsyncFunction() throws an error
    }
}

2. Object.values()

이 함수가 나오면서 객체 사용이 훨씬 편해졌습니다.

const exampleObj = {a: 1, b: 2, c: 3, d:4};
console.log(Object.value(exampleObj)); // [1, 2, 3, 4];

// To do the same thing before, use the following notation. much verbose
const values = Object.keys(exampleObj).map(key => exampleObj[key]);

3. Object.entries()

이 함수가 아마 가장 유용한 함수가 아닐까 싶네요.

const exampleObj = {a: 1, b: 2, c: 3, d:4};
console.log(Object.entries(exampleObj)); // [["a", 1], ["b", 2], ["c", 3], ["d", 4]];

// 가장 많이 쓰이는 예
for (const [key, value] of Object.entries(exampleObj)) {
	console.log(`key: ${key}, value: ${value}`);
}
// key: a, value: 1
// key: b, value: 2
// key: c, value: 3
// key: d, value: 4

4. String padStart() & padEnd()

pad는 좌우에 특별한 문자열로 채우는 기능을 합니다. 달력의 월을 두 자리 숫자로 만들 수 있고 아이디를 표현할 때 끝내 몇 문자를 * 별표로 변환할 수 있습니다.

String.padStart(fillingLength, FillingContent);
// 첫 번째 파라미터인 filllingLength가 문자열의 길이가 fillingLength보다 작을 경우 그 나머지에 두번째 파라미터인 FillingContent를 채워 넣는 함수입니다.
// padStart
'100'.padStart(5, 0); // 00100
// 5칸인데 문자열이 3칸이라 앞에 0을 두 번 붙임.

'100'.padStart(5, '987'); // 98100
// 5칸인데 문자열이 3칸이라 앞에 987을 넣으면 되는데 남는 칸은 2칸인데 987이 세 칸이라 98만 적용됨

// padEnd
'100'.padEnd(5, 9); // 10099
// padStart와는 반대로 끝에서 덧 붙임.
'100'.padEnd(5, '987'); // 10098

// 아이디 별표 표시하기
const id = '아이디입니다'
const temp = id.slice(0, 3);
const result = temp.padEnd(id.length, '*');

console.log(result); // 아이디***

5. Trailing commas

배열 마지막에는 콤마를 못 썼었는데요, 이제는 쓸 수 있습니다.

[ "foo",    "baz",    "bar",    "baz",]

6. Object.getOwnPropertyDescriptors()

객체의 각 항목에 대한 Descriptor를 표시해 줍니다.

const exampleObj = {a: 1, b: 2, c: 3, d:4};

Object.getOwnPropertyDescriptors(exampleObj);
// {a: {}, b: {}, c: {}, d: {}}
// a: {value: 1, writable: true, enumerable: true, configurable: true}
// b: {value: 2, writable: true, enumerable: true, configurable: true}
// c: {value: 3, writable: true, enumerable: true, configurable: true}
// d: {value: 4, writable: true, enumerable: true, configurable: true}
// __proto__: Object

7. SharedArrayBuffer

8. Atomics object


# ES9 (ES2018)

  1. await in loop
async function process(array) {
  for (const i of array) {
    await doSomething(i);
  }
}

async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}

위 코드는 예상한 대로 실행되지 않을 겁니다.

그래서 ES9에서는 아래와 같이 비동기 이터레이터를 도입했습니다.

async function process(array) {
  for await (const i of array) {
    doSomething(i);
  }
}

2. promise.finally()

function process() {
  process1()
  .then(process2)
  .then(process3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    console.log(`it must execut no matter success or fail`);
  });
}

3. Rest, Spread

나머지 매개변수와 스프레드 연산자는 다들 React에서 잘 쓰고 계시죠?

function restParams(p1, p2, ...p3) {
    console.log(p1); // 1
    console.log(p2); // 2
    console.log(p3); // [3, 4, 5]
}
restParams(1, 2, 3, 4, 5);
const values = [19, 90, -2, 6, 25];
console.log( Math.max(...values) ); // 90
const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...r } = myObject;
// a = 1
// r = { b: 2, c: 3 }

// Can also be used in function input parameters
function restObjectInParam({ a, ...r }) {
    console.log(a); // 1
    console.log(r); // {b: 2, c: 3}
}

restObjectInParam({
  a: 1,
  b: 2,
  c: 3
});

4. RegExp groups

  1. Regexp lookahead Negative

6. Regexp dotAll


# ES10 (ES2019)

  1. Better friendly JSON.stringify

유니코드를 좀 더 완벽하게 지원하게 되었습니다.

2. Array.prototype.flat() & Array.prototype.flatMap()

reduce를 이용 안 해도 다음과 같이 flat, flatMap을 이용하면 됩니다.

const arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]
// Pass in a number in flat, representing the flattening depth
arr2.flat(2); // [1, 2, 3, 4, 5, 6]
let arr = ["早安", "", "今天天氣不錯"]

arr.map(s => s.split(""))
// [["早", "安"], [""], ["今", "天", "天", "氣", "不", "錯"]]

arr.flatMap(s => s.split(''));
// ["早", "安", "", "今", "天", "天", "氣", "不", "錯"]

3. String.prototype.trimStart() & String.prototype.trimEnd()

The trimStart() 메서드는 문자열의 처음부터 화이트 스페이스를 제거하는 함수입니다.

trimLeft()sms trimStart()의 alias로 같은 함수입니다.

const greeting = ‘ Hello world! ‘;console.log(greeting);
// expected output: “ Hello world! “;console.log(greeting.trimStart());
// expected output: “Hello world! “;

trimEnd() 메서드는 반대로 문자열 끝에서부터 화이트 스페이스를 제거합니다.

trimRight() 는 trimEnd() 함수의 alias입니다.

const greeting = '   Hello world!   ';console.log(greeting);
// expected output: "   Hello world!   ";console.log(greeting.trimEnd());
// expected output: "   Hello world!";

4. Object.fromEntries()

Object.fromEntries() 메서드는 키, 밸류 형태의 배열을 해당 객체로 변환합니다.

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// Object { foo: "bar", baz: 42 }

5. String.prototype.matchAll

const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
const array = [...str.matchAll(regexp)];
console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]
console.log(array[1]);
// expected output: Array ["test2", "e", "st2", "2"]

6. fixed catch bind

e 파라미터를 생략할 수 있습니다.

try {...} catch(e) {...}

// If e is not used, it can be omitted
try {...} catch {...}

7. BigInt (new number type)

primitive 타입으로 bigint 타입이 추가됨, 숫자 끝에 n을 붙이면 됨.

또는 BitInt() 함수를 이용해도 됨

ES5: String, Number, Boolean, Null, Undefined 총 5 타입

ES6 Added: Symbol, 총 6 타입

ES10 added: BigInt, 총 7 타입

const theBiggestInt = 9007199254740991n;

const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n

const hugeString = BigInt("9007199254740991");
// ↪ 9007199254740991n

const hugeHex = BigInt("0x1fffffffffffff");
// ↪ 9007199254740991n

const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
// ↪ 9007199254740991n


# ES11 (ES2020)

  1. Promise.allSettled

프로미스가 각각 관계가 없을 때 사용.

Promise.all()은 반대로 각각의 프로미스가 관계가 있을 때 사용.

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"

2. Optional chaining ?

React 코드에 많이 쓰입니다.

const isUserExist = user && user.info;
if (isUserExist) {
    username = user.info.name;
}

위 코드는 아래와 같이 간략하게 사용할 수 있게 됐습니다.

const username = user?.info?.name;

이에 덧붙여 undefined 일 때 기본값을 || 를 이용해서 지정할 수 있습니다.

const username = user?.name || 'guest';

3. Nullish coalescing operator ??

자바스크립트에서는 0, null, undefined는 false로 자동으로 변환되는데, 가끔 진짜 0이라는 값을 가질 때는 애매해집니다.

이때 0이라는 숫자를 별도로 취급해준다고 하면 아래와 같이 긴 코드를 써야 하죠.

/**
 * user = {
 *    level: 0
 * }
 */
const level = user.level || 'no level'; // output as no level instead of expected 0
// to fix it, it must use if simple processing
const level = user.level !== undefined && user.level !== null ? user.level : 'no level';

??를 이용하면 아래와 같이 간단하게 사용할 수 있습니다.

const username = user.level ?? 'no level';
// output 0. if level is not available, it becomes 'no level'.

4. Dynamic-import

다이내믹 임포트 뜻 그래도 필요할 때만 로드할 수 있습니다.

el.onclick = () => {
    import(`/js/current-logic.js`)
    .then((module) => {
        module.doSomthing();
    })
    .catch((err) => {
        handleError(err);
    })
}

5. GlobalThis

globalThis 객체는 global this를 가리킵니다.

예전에는 아래와 같이 했지만

// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/globalThis
const getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

var globals = getGlobal();

이제 아래와 같이 간단하게 사용할 수 있습니다.

function canMakeHTTPRequest() {
  return typeof globalThis.XMLHttpRequest === 'function';
}

console.log(canMakeHTTPRequest());
// expected output (in a browser): true


# ES12 (ES2021)

  1. Promise.any
const p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('p1 resolved value')
  }, 1000)
})const p2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('p2 resolved value')
  }, 500)
})const p3 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('p3 resolved value')
  }, 1800)
})Promise.any([p1, p2, p3]).then(value=>{
  console.log(value)
}) // p2 resolved value

2. Logical Assignment Operator

||= , &&= , ??=는  +=과 같은 방식으로 동작합니다.

let b = 2
b += 1
// equal to b = b + 1let a = null
a ||= 'some random text'  // a become to'some random text'
// equal a = a || 'some random text'let c = 'some random texts'
c &&= null  // c become to null
// equal to c = c && nulllet d = null
d ??= false  // d become to false
// equal to d = d ?? false

3. WeakRef

class Counter {
  constructor(element) {
    // Remember a weak reference to the DOM element
    this.ref = new WeakRef(element);
    this.start();
  }

  start() {
    if (this.timer) {
      return;
    }

    this.count = 0;

    const tick = () => {
      // Get the element from the weak reference, if it still exists
      const element = this.ref.deref();
      if (element) {
        element.textContent = ++this.count;
      } else {
        // The element doesn't exist anymore
        console.log("The element is gone.");
        this.stop();
        this.ref = null;
      }
    };

    tick();
    this.timer = setInterval(tick, 1000);
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0;
    }
  }
}

const counter = new Counter(document.getElementById("counter"));
setTimeout(() => {
  document.getElementById("counter").remove();
}, 5000);

지금까지 ES6에서 ES12까지 추가된 걸 살펴보았는데요.

자바스크립트는 현실에 안주하지 않고 계속 발전해 나가는 언어라서 제가 생각해도 미래가 밝다고 봅니다.

특히 React 진영의 견고함이 상당한데요.

많은 도움이 되셨으면 합니다.

그럼.