Skip to content

Latest commit

 

History

History
605 lines (490 loc) · 15.6 KB

README.md

File metadata and controls

605 lines (490 loc) · 15.6 KB

소개

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)