Skip to content

marocchino/es6features

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 

Repository files navigation

소개

ECMAScript 6(이하 ES6)또는 ECMAScript 2015는 ECMAScript 표준의 다음 버전이다. 2015년 6월 승인을 목표로 하고 있다. ES6은 2009년 표준화된 ES5 이후 언어에서 처음으로 중요한 업데이트이다. 주요 자바스크립트 엔진들은 현재 이 기능들의 구현이 진행중이다.

ES6의 전체 표준의 제안을 확인해볼 수 있다.

ES6는 다음과 같은 새로운 기능들을 포함한다.

ES6 기능

Arrows

=> 문법은 함수의 단축표기이다. C#, Java 8, CoffeeScript와 문법적으로 비슷하다. 표현식과 괄호식(statement body) 모두 지원한다. 함수와는 다르게 this를 바깥에서 가져온다.

// 표현식
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));

// 괄호식
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

// Lexical this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
}

Classes

ES6 클래스는 프로토타입 기반의 객체지향 패턴보다 쓰기 쉽다. 한줄짜리 코드로 클래스를 더 사용하기 쉽고 상호유용성(interoperability)이 좋아진다. 클래스는 프로토타입 기반의 상속, 부모 호출, 상속, 그리고 스태틱 메소드, 생성자를 지원한다.

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  get boneCount() {
    return this.bones.length;
  }
  set matrixType(matrixType) {
    this.idMatrix = SkinnedMesh[matrixType]();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

Enhanced Object Literals

객체 표현에 생성 시 사용할 프로토타입 설정, foo: foo의 단축표기, 메소드 정의 및 부모 호출, 표현식과 함께 계산된 프로퍼티 이름 등의 지원이 추가되었다. 게다가 객체 표현을 클래스 선언에 가깝게 사용할 수 있으며 객체지향 설계의 장점도 가져올 수 있다.

var obj = {
   // 프로토타입 설정
   __proto__: theProtoObj,
   // `handler: handler`의 단축 표기
   handler,
   // 메소드
   toString() {
     // 부모 호출
     return "d " + super.toString();
   }
};

Template Strings

템플릿 스트링은 문자열 생성을 위한 문법적인 편의이다. 펄, 파이썬 등의 스트링 인터폴레이션 기능과 비슷하다. 인젝션 공격을 피하거나 문자열 내용을 통해 고차원의 자료구조를 생성하는데 사용할 수 있다.

// 기본적인 문자열 생성
`In JavaScript '\n' is a line-feed.`

// 여러줄의 문자열
`In JavaScript this is
 not legal.`

// 문자열 보간
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// HTTP 요청을 위해 필요한 정보 치환
GET`http://foo.org/bar?a=${a}&b=${b}
    Content-Type: application/json
    X-Credentials: ${credentials}
    { "foo": ${foo},
      "bar": ${bar}}`(myOnReadyStateChangeHandler);

Destructuring

배열이나 객체에서 패턴매칭을 통한 바인드를 지원한다. 실패 완화(fail-soft)는 객체에서 속성을 찾는 방법 foo["bar"]와 비슷해서, 못찾으면 undefined가 된다.

  // 배열의 매칭
  var [a, , b] = [1,2,3];

  // 객체의 매칭
  var { op: a, lhs: { op: b }, rhs: c }
         = getASTNode()

  // 객체 매칭의 단축표기
  // 스코프에서 `op`, `lhs`, 그리고 `rhs`를 바인드
  var {op, lhs, rhs} = getASTNode()

  // 파라미터 위치에서도 쓸 수 있다
  function g({name: x}) {
    console.log(x);
  }
  g({name: 5})

  // 실패 완화의 분해
  var [a] = [];
  a === undefined;

  // 기본값을 사용한 실패 완화의 분해
  var [a = 1] = [];
  a === 1;

Default + Rest + Spread

함수가 정의될 때 기본값을 설정할 수 있다. 뒤따라오는 인자들을 하나의 배열로 만들어준다. 배열을 직접 arguments처럼 사용할 수 있다.

function f(x, y=12) {
  // 넘긴 값이 없을 때(혹은 undefined를 넘길 때) y는 12
  return x + y;
}
f(3) == 15
function f(x, ...y) {
  // y는 배열
  return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
  return x + y + z;
}
// 배열의 인자 각각을 인자로 넘긴다.
f(...[1,2,3]) == 6

Let + Const

괄호({}) 안의 스코프 안에서 유효한 let은 새로운 var이다. const는 선언 뒤에 값이 변경되는걸 막아준다.

function f() {
  {
    let x;
    {
      // okay, block scoped name
      const x = "sneaky";
      // error, const
      x = "foo";
    }
    // error, already declared in block
    let x = "inner";
  }
}

Iterators + For..Of

iterator는 공통 언어 런타임(CLR; Common Language Runtime)의 IEnumerable이나 Java의 Iterable과 비슷한 커스텀 이터레이터를 만들어준다. for-in처럼 커스텀된 이터레이터는 for-of를 사용한다. 실제로 배열을 만들 필요 없이, LINQ처럼 나중에 만들 수 있는(lazy) 디자인 패턴이다.

let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return { done: false, value: cur }
      }
    }
  }
}

for (var n of fibonacci) {
  // 1000번째에서 자르기
  if (n > 1000)
    break;
  console.log(n);
}

이터레이터의 인터페이스를 TypeScript의 문법으로 표현하자면

interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;
}
interface Iterable {
  [Symbol.iterator](): Iterator
}

Generators

간단하게 말해서 function*yield로 이터레이터를 짜는 것이다. function*로 함수를 선언하면 제너레이터 인스턴스를 리턴한다. 제너레이터는 nextthrow가 추가된 이터레이터의 서브타입이다. 제너레이터를 통해 값을 던지는데, yield는 값을 리턴하는(혹은 던지는) 표현식이다.

노트: 'await'같은 비동기 프로그래밍도 가능하며, ES7에서는 await가 제안되었다.

var fibonacci = {
  [Symbol.iterator]: function*() {
    var pre = 0, cur = 1;
    for (;;) {
      var temp = pre;
      pre = cur;
      cur += temp;
      yield cur;
    }
  }
}

for (var n of fibonacci) {
  // 1000번째에서 자르기
  if (n > 1000)
    break;
  console.log(n);
}

제너레이터의 인터페이스를 TypeScript의 문법으로 표현하자면

interface Generator extends Iterator {
    next(value?: any): IteratorResult;
    throw(exception: any);
}

Unicode

문자열에서의 새로운 유니코드 표현식과 정규식에서 u 모드를 포함해 전체 유니코드를 지원한다. 게다가 21bit도 문자열로 처리할 수 있는 새로운 API도 제공한다. 이런 추가들은 자바스크립트로 글로벌 앱을 만드는데 도움이 된다.

// same as ES5.1
"𠮷".length == 2

// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2

// new form
"\u{20BB7}"=="𠮷"=="\uD842\uDFB7"

// new String ops
"𠮷".codePointAt(0) == 0x20BB7

// for-of iterates code points
for(var c of "𠮷") {
  console.log(c);
}

Modules

언어 차원에서 컴포넌트로 선언된 모듈을 지원한다. 암묵적으로 비동기 모델로 돌아가며, 요청받은 모듈을 실행할 때까지 코드가 실행되지 않는다.

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));

export defaultexport *같은 기능들도 있다.

// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
    return Math.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathplusplus";
alert("2π = " + ln(e)*pi*2);

Module Loaders

모듈 로더는 다음 기능들을 지원한다.

  • 동적 로딩
  • 상태 고립
  • 글로벌 네임스페이스 고립
  • 컴파일 후킹
  • 중첩된 가상화

기본 모듈 로더는 설정이 가능하고, 새로운 로더로 실행하는 것도 가능하고 고립되어 로딩되거나 컨텍스트를 제한해서 로딩할 수도 있다.

// 다이나믹 로딩 - `System`이 기본 로더
System.import('lib/math').then(function(m) {
  alert("2π = " + m.sum(m.pi, m.pi));
});

// 새 로더 생성 - 샌드박스 실행
var loader = new Loader({
  global: fixup(window) // replace ‘console.log’
});
loader.eval("console.log('hello world!');");

// 직접 모듈 캐시를 관리
System.get('jquery');
System.set('jquery', Module({$: $})); // 경고: 아직 완료되지 않음

Map + Set + WeakMap + WeakSet

흔히 쓰는 알고리즘을 위한 효율적인 자료 구조들. WeakMap은 누수없이 객체를 키로 갖는 테이블을 만들 수 있다.

// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });

Proxies

객체가 할 수 있는 모든 행동을 다루게 해준다. 주입, 객체 가상화, 로그/프로파일링 등에서 사용될 수 있다.

// 보통 객체에 대한 프록시
var target = {};
var handler = {
  get: function (receiver, name) {
    return `Hello, ${name}!`;
  }
};

var p = new Proxy(target, handler);
p.world === 'Hello, world!';
// 함수 객체에 대한 프록시
var target = function () { return 'I am the target'; };
var handler = {
  apply: function (receiver, ...args) {
    return 'I am the proxy';
  }
};

var p = new Proxy(target, handler);
p() === 'I am the proxy';

런타임 단계에서 사용할 수 있는 핸들러들이다

var handler = {
  get:...,
  set:...,
  has:...,
  deleteProperty:...,
  apply:...,
  construct:...,
  getOwnPropertyDescriptor:...,
  defineProperty:...,
  getPrototypeOf:...,
  setPrototypeOf:...,
  enumerate:...,
  ownKeys:...,
  preventExtensions:...,
  isExtensible:...
}

Symbols

심볼은 새로운 프리미티브 타입니다. (ES5에서처럼) string이나 symbol을 키로 속성을 다룰 수 있다. 심볼은 유니크하지만 Object.getOwnPropertySymbols 같은 기능을 통해 노출되기 때문에 private는 아니다.

var MyClass = (function() {

  // module scoped symbol
  var key = Symbol("key");

  function MyClass(privateData) {
    this[key] = privateData;
  }

  MyClass.prototype = {
    doStuff: function() {
      ... this[key] ...
    }
  };

  return MyClass;
})();

var c = new MyClass("hello")
c["key"] === undefined

Subclassable Built-ins

ES6에서는 Array, Date, DOM Element같은 내장 객체의 서브클래스를 만들 수 있다.

Ctor라는 이름의 함수로 객체를 생성하는데 (가상으로 실행되는) 2단계가 있다.

  • Ctor[@@create]를 불러 객체를 할당하고 특정 행동들을 주입한다
  • 초기화할 새 인스턴스의 생성자를 실행한다.

@@create라고 불리는 명령어는 Symbol.create를 통해서 이루어진다. 내장된 속성들은 @@create를 통해 명시적으로 드러난다.

// Array의 의사 코드
class Array {
    constructor(...args) { /* ... */ }
    static [Symbol.create]() {
        // [[DefineOwnProperty]]를 주입
        // 'length'에 대한 갖가지 업데이트
    }
}

// Array의 서브클래스에 대한 유저 구현
class MyArray extends Array {
    constructor(...args) { super(...args); }
}

// 두 단계의 'new' :
// 1) @@create를 호출해서 객체를 할당한다.
// 2) 새로운 인스턴스에 생성자를 실행시킨다.
var arr = new MyArray();
arr[1] = 12;
arr.length == 2

Math + Number + String + Array + Object APIs

코어 Math 라이브러리, 각종 Array 유틸, 문자열 유틸, 객체 복사를 위한 Object.assign 등 새로운 라이브러리들이 추가되었다.

Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll('*')) // 진짜 Array로 리턴
Array.of(1, 2, 3) // [1, 2, 3]
[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0,0) })

Binary and Octal Literals

2진표기b와 8진표기o

0b111110111 === 503 // true
0o767 === 503 // true

Promises

프로미스는 비동기 프로그래밍을 위한 라이브러리다. 향후 사용할지 모르는 값을 나타내는데 쓰는 일급 표현이다. 많은 자바스크립트 라이브러리들이 이미 쓰이고 있다.

function timeout(duration = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, duration);
    })
}

var p = timeout(1000).then(() => {
    return timeout(2000);
}).then(() => {
    throw new Error("hmm");
}).catch(err => {
    return Promise.all([timeout(100), timeout(200)]);
})

Reflect API

런타임 단계에서 객체의 행동들을 드러내는 API이다. 프록시 API의 반대이며 프록시를 구현하는데 특히 유용하다.

// 아직 샘플 없음

Tail Calls

함수의 끝에서의 호출(꼬리 재귀)는 스택을 더 만들지 않도록 해준다. 길이를 모르는 입력에 대해 안전하게 재귀호출을 만드는 알고리즘이다.

function factorial(n, acc = 1) {
    'use strict';
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc);
}

// 요즘 대부분 구현체들에서는 스택 오버플로우
// 하지만 ES6에서는 어떤 입력에서도 안전하다.
factorial(100000)

About

Overview of ECMAScript 6 features

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published