Record
ECMAScript 6
Introduction
ECMAScript 2015로도 알려져 있는 ECMAScript 6는 ECMAScript 표준의 가장 최신 버전입니다. ES6는 새로운 언어 기능이 포함된 주요 업데이트이며, 2009년도에 표준화된 ES5 이후로 언어 기능에 대한 첫 업데이트이기도 합니다. 현재 주요 JavaScript 엔진들에서 ES6 기능들을 구현 중 141에 있습니다.
ECMAScript 6 언어의 전체 스펙을 확인하시려면 ES6 Standard 349를 확인하세요.
ES6 includes the following new features:
- arrows
- classes
- enhanced object literals
- template strings
- destructuring
- default + rest + spread
- let + const
- iterators + for..of
- generators
- unicode
- modules
- module loaders
- map + set + weakmap + weakset
- proxies
- symbols
- subclassable built-ins
- promises
- math + number + string + array + object APIs
- binary and octal literals
- reflect api
- tail calls
- Deploying to Github Pages
- 즉시호출함수, 즉시실행함수 IIFE Immediately-invoked function expression
- 시간 측정
ECMAScript 6 Features
Arrows
Arrows(화살표) 함수는 => 문법을 사용하는 축약형 함수입니다. C#, Java 8, CoffeeScript의 해당 기능과 문법적으로 유사합니다. Arrows는 표현식의 결과 값을 반환하는 표현식 본문(expression bodies)뿐만 아니라 상태 블럭 본문(statement block bodies)도 지원합니다. 하지만 일반 함수의 자신을 호출하는 객체를 가리키는 dynamic this와 달리 arrows 함수는 코드의 상위 스코프(lexical scope)를 가리키는 lexical this를 가집니다.
// Expression bodies (표현식의 결과가 반환됨)
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}));
// Statement bodies (블럭 내부를 실행만 함, 반환을 위해선 return을 명시)
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Lexical this
// 출력결과 : Bob knows John, Brian
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
}
printFriends() 함수의 서브루틴은 다음과 문법상 동일하게 동작합니다.
this._friends.forEach(function (f) {
console.log(this._name + " knows " + f);
}.bind(this));
More info: MDN Arrow Functions
printFriends() 함수의 선언 표기법이 궁금하시면 MDN Object Initializer 112를 참고하세요.
Classes
ES6 클래스는 포로토타입 기반 객체지향 패턴을 더 쉽게 사용할 수 있는 대체재입니다. 클래스 패턴 생성을 더 쉽고 단순하게 생성할 수 있어서 사용하기도 편하고 상호운용성도 증가됩니다.
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();
}
}
More info: MDN Classes
Enhanced Object Literals
ES6에서 객체 리터럴은 선언문에서 프로토타입 설정, foo: foo 선언을 위한 단축 표기법, 메서드 정의, super 클래스 호출 및 동적 속성명을 지원하도록 향상 되었습니다. 그에 따라 객체 리터럴 및 클래스 선언이 더 밀접되어져, 객체기반 설계가 더 편리해졌습니다.
var obj = {
// __proto__
__proto__: theProtoObj,
// Shorthand for ‘handler: handler’
// ‘handler: handler’의 단축 표기
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ 'prop_' + (() => 42)() ]: 42
};
More info: MDN Grammar and types: Object literals
Template Strings
Template Strings(ES6 부터는 Template literals라 부름)는 문법적으로 더 편하게 string을 생성할 수 있게 합니다. 이는 Perl, Python 등의 문자열 보간(string interpolation)과 유사합니다. Tagged template literals는 인젝션 공격 방어 혹은 문자열로 부터 상위 데이터 구조체 재조립 등을 위해 string 생성을 커스터마이징이 가능하게끔 해줍니다.
// Basic literal string creation
`In JavaScript '\n' is a line-feed.`
// Multiline strings
`In JavaScript this is
not legal.`
// String interpolation
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
<div class="modal-content">
${
//we're just going to wrap an anonymous inline function here and then call it with some data
(job => job ? `<h4>Edit Job</h4>` : `<h4>Create Job</h4>` )(data.job) //call the anonymous inline with the data we care about
}
</div>
// Construct an HTTP request prefix is used to interpret the replacements and construction
POST`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
More info: MDN Template Strings
Destructuring
Destructuring는 배열과 객체에 패턴 매칭을 통한 데이터 바인딩을 제공합니다. Destructuring는 할당 실패에 유연하며, 실패 시 undefined 값이 자동할당 됩니다. 또한 foo[“bar”]와 같이 객체의 속성 값도 자동으로 검색하여 바인딩해줍니다.
// list matching
var [a, , b] = [1,2,3];
// object matching
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// object matching shorthand
// object matching 단축 표기
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()
// Can be used in parameter position
// parameter에서도 사용 가능
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Fail-soft destructuring
var [a] = [];
a === undefined;
// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;
More info: MDN Destructuring assignment
Default + Rest + Spread
파라미터에 기본 값을 설정할 수 있습니다.
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15
가변인자를 사용가능하며, 배열로 치환시켜 줍니다. Rest parameters는 arguments 보다 직관성을 제공합니다.
function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6
함수 호출 시 배열을 일련의 인자에 나누어 주입시켜 줍니다.
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
More MDN info: Default parameters, Rest parameters, Spread Operator
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";
}
}
var의 유효 범위는 전체 외부 함수까지이지만 let은 변수를 선언한 블록과 그 내부 블록들에서 유효합니다.
function varTest() {
var x = 31;
if (true) {
var x = 71; // same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // different variable
console.log(x); // 71
}
console.log(x); // 31
}
function varTest() {
if (true) {
var x = 71;
console.log(x); // 71
}
console.log(x); // 71
}
function varTest() {
let x = 71;
if (true) {
console.log(x); // 71
}
console.log(x); // 71
}
function varTest() {
if (true) {
let x = 71;
console.log(x); // 71
}
console.log(x); // Uncaught ReferenceError: x is not defined
}
More MDN info: let statement, const statement
Iterators + For..Of
Iterator 객체는 CLR의 IEnumerable 혹은 Java의 Iterable처럼 사용자 정의의 반복을 가능하게 해줍니다. for..of 반복문이 ES6에서 추가 되었으며 for..in 반복문과 달리 iterator 기반의 컬렉션 전용 반복문입니다. for in 반복문과의 차이점은 for in vs for of 341를 참고하세요.
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) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
Iteration은 아래의 duck-type 인터페이스를 기반으로 합니다. (설명을 위해 TypeScript 55의 타입 문법을 사용하였습니다)
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
More info: MDN for…of
Generators
Generators는 function와 yield 키워드를 이용하여 iterator 선언을 단순하게 작성할 수 있게 도와줍니다. function로 선언한 함수는 Generator 객체를 반환합니다. Generators는 iterator의 하위 타입이며 next와 throw 메서드를 가지고 있습니다. 이 메서드들로 인해 yield 키워드로 반환된 값은 다시 generator에 주입거나 예외처리를 할 수 있게 되었습니다.
참고: 해당 키워드는 비동기 프로그래밍의 ‘await’ 같은 기능이 가능하게끔 기반이 되었습니다. ES7의 await 제안서 46를 참고하세요.
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) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
function* gen(){
yield* ["a", "b", "c"];
}
var a = gen();
a.next(); // { value: "a", done: false }
a.next(); // { value: "b", done: false }
a.next(); // { value: "c", done: false }
a.next(); // { value: undefined, done: true }
generator 인터페이스는 다음과 같습니다. (설명을 위해 TypeScript 55의 타입 문법을 사용하였습니다)
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
More info: MDN Iteration protocols
Unicode
완전한 유니코드를 지원하기 위해 문자열에 새로운 유니코드 리터럴과 정규표현식에 u 모드가 추가되었습니다. 또한 21비트 형식까지 처리하기 위한 신규 API가 추가되었습니다. 이 추가된 기능은 JavaScript로 글로벌 앱을 만들 수 있도록 지원합니다.
// 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);
}
More info: MDN RegExp.prototype.unicode
Modules
언어 차원에서 컴포넌트 정의를 위한 모듈을 지원합니다. 유명한 JavaScript 모듈 로더들(AMD, CommonJS)의 패턴을 적용시켰습니다. 런타임 동작은 호스트에 정의된 기본 로더에 의해 정의됩니다. 묵시적 비동기 형태로 요구되는 모듈들이 정상적으로 로드되기 전까지 코드가 실행되지 않습니다.
// 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 default와 export * 문법도 제공합니다.
// 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);
More MDN info: import statement, export statement
Module Loaders
Module Loaders는 다음을 지원합니다.
- 동적 로딍(Dynamic loading)
- 상태 격리(State isolation)
- 전역 네임스페이스 격리(Global namespace isolation)
- 컴파일 훅(Compilation hooks)
- 중첩 가상화(Nested virtualization)
기본으로 사용할 모듈 로더를 설정할 수 있으며, 로더를 새로 생성하여 격리되거나 제한된 맥락에서 코드를 로드할 수 있습니다.
// Dynamic loading – ‘System’ is default loader
// 동적 로딩 – ‘System’ is default loader
System.import('lib/math').then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Create execution sandboxes – new Loaders
// 실행 샌드박스 생성 – new Loaders
var loader = new Loader({
global: fixup(window) // replace ‘console.log’
});
loader.eval("console.log('hello world!');");
// Directly manipulate module cache
// 모듈 캐시 직접 조작
System.get('jquery');
System.set('jquery', Module({$: $})); // WARNING: not yet finalized
Map + Set + WeakMap + WeakSet
일반 알고리즘을 위한 효율적인 데이터 구조를 제공합니다. WeakMap과 WeakSet는 메모리 누수로 부터 자유롭게 해줍니다. 이들 내 저장된 객체에 다른 참조가 없는 경우, garbage collection 될 수 있습니다.
// 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 });
// Because the added object has no other references, it will not be held in the set
More MDN info: Map, Set, WeakMap, WeakSet
Proxies
프록시(Proxy)를 사용하면 호스트 객체에 다양한 기능을 추가하여 객체를 생성할 수 있습니다. interception, 객체 추상화, 로깅/수집, 값 검증 등에 사용될 수 있습니다.
구문
- new Proxy(target, handler);
- handler : trap들을 가지고 있는 Placeholder 객체.
- traps : 프로퍼티에 접근할 수 있는 메소드. 운영체제에서 trap 이라는 컨셉과 유사하다.
- target : proxy가 가상화하는 실제 객체. 이것은 proxy를 위한 backend 저장소로 사용된다
// Proxying a normal object
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === 'Hello, world!';
// Proxying a function object
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';
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// The default behavior to store the value
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
console.log(person.age); // 100
person.age = 'young'; // Throws an exception
person.age = 300; // Throws an exception
proxy의 handler가 가질 수 있는 트랩(trap)들입니다. There are traps available for all of the runtime-level meta-operations:
var handler =
{
get:...,
set:...,
has:...,
deleteProperty:...,
apply:...,
construct:...,
getOwnPropertyDescriptor:...,
defineProperty:...,
getPrototypeOf:...,
setPrototypeOf:...,
enumerate:...,
ownKeys:...,
preventExtensions:...,
isExtensible:...
}
More info: MDN Proxy
Symbols
심볼(Symbol)은 객체 상태의 접근 제어를 가능하게 합니다. Symbol은 새로운 원시 타입으로 이름 충돌의 위험 없이 속성(property)의 키(key)로 사용할 수 있습니다. 옵션 파라미터인 description는 디버깅 용도로 사용되며 식별 용도는 아닙니다. Symbol은 고유(unique)하며, Object.getOwnPropertySymbols와 같은 reflection 기능들로 접근할 수 있기 때문에 private 하진 않습니다(for in나 Object.keys()로는 접근 불가).
- 심볼 타입은 주로 고유한 객체의 프로퍼티의 값으로 사용하는 목적으로 쓰입니다.
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
More info: MDN Symbol
Subclassable Built-ins
ES6에서 Array, Date, DOM Element 같이 내장 객체들은 상속이 가능합니다. 객체 생성 시 호출되는 Ctor 함수는 다음의 2단계를 가집니다.(둘다 가상적으로 실행)
- 객체 할당을 위해
Ctor[@@create]
호출하여 - 새로운 인스턴스의 생성자를 호출해 초기화 진행
아시다싶이 @@create
심볼은 Symbol.create
를 통해 만들어졌습니다.
// Pseudo-code of Array
class Array {
constructor(...args) { /* ... */ }
static [Symbol.create]() {
// Install special [[DefineOwnProperty]]
// to magically update 'length'
}
}
// User code of Array subclass
class MyArray extends Array {
constructor(...args) { super(...args); }
}
// Two-phase 'new':
// 1) Call @@create to allocate object
// 2) Invoke constructor on new instance
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
Math + Number + String + Array + Object APIs
core Math 라이브러리, Array 생성 helper, String helper, 복사를 위한 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('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[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) })
More MDN info: Number, Math, Array.from, Array.of, Array.prototype.copyWithin, Object.assign
Binary and Octal Literals
2진법 (b
), 8진법 (o
) numeric 리터럴 형식이 추가되었습니다.
0b111110111 === 503 // true
0o767 === 503 // true
Promises
Promise는 비동기 프로그래밍을 위한 라이브러리입니다. Promise는 미래에 생성되는 값을 나타내는 일급 객체입니다. Promise는 현존하는 많은 JavaScript 라이브러리에 사용되고 있습니다.
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)]);
})
//프로미스 생성
const promise1 = function(param){
return new Promise(function(resolve,reject){
if(param){
resolve("바보");
}
else{
reject("아닌데");
}
});
}
//프로미스 실행
promise1(true).then(function(result){
console.log(result);//바보
},function(err){
console.log(err);//아닌데
});
_promise(true)
.then(JSON.parse)
.catch(function () {
window.alert('체이닝 중간에 에러가!!');
})
.then(function (text) {
console.log(text);
});
More info: MDN Promise
Reflect API
Reflection API는 런타임 시 객체에 대해 작업을 수행할 수 있습니다. 프록시 트랩(proxy traps)와 같은 메타 함수들을 가지고 있습니다. Reflection은 프록시를 구현하는데 유용합니다.
class Greeting {
constructor(name) {
this.name = name;
}
greet() {
return `Hello ${name}`;
}
}
function greetingFactory(name) {
return Reflect.construct(Greeting, [name], Greeting);
}
greetingFactory('a'); // Greeting {name: "a"}
const object1 = {};
Reflect.set(object1, 'property1', 42);
console.log(object1.property1);
// expected output: 42
const array1 = ['duck', 'duck', 'duck'];
Reflect.set(array1, 2, 'goose');
console.log(array1[2]);
// expected output: "goose"
More info: MDN Reflect
Tail Calls
마지막에 호출되는 함수가 호출 스택이 초과되게 하지 않습니다. 재귀 알고리즘을 매우 큰 입력 값에서도 안전하게 만듭니다.
function factorial(n, acc = 1) {
'use strict';
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// Stack overflow in most implementations today,
// but safe on arbitrary inputs in ES6
// 현재 대부분의 자바스크립트 엔진에서 스택 오버플로우가 일어나지만,
// ES6에서는 입력 값이 커도 안전하다
factorial(100000)
Deploying to Github Pages
npm i gh-pages
package.json “homepage”:”https://mamma124.github.io/project_2020” “scripts”: “build” : “react-scripts build” “deploy” :”gh-pages -d build” “predeploy”: “npm run build”
즉시호출함수, 즉시실행함수 IIFE (Immediately-invoked function expression)
- 함수를 정의하자마자 바로 호출하는 것을 즉시 실행 함수
(function () {
// statements
})()
- 기명 즉시 실행 함수
(function square(x) {
console.log(x*x);
})(2);
(function square(x) {
console.log(x*x);
}(2));
위의 두가지 예는 괄호의 위치가 조금 다를 뿐 같은 기능의 즉시 실행 함수 입니다.
- 익명 즉시 실행 함수
(function (x) {
console.log(x*x);
})(2);
(function (x) {
console.log(x*x);
}(2));
- 변수에 즉시 실행 함수 저장
(mySquare = function (x) {
console.log(x * x);
})(2);
mySquare(3);
console.log("fn call", a);
return a;
};
const fn2 = function test(x) {
console.log("fn2 call", x);
x();
return fn;
};
fn2(() => {
console.log("middlewar");
})("1");
$ fn2 call
$ ƒ () {}
$ middlewar
$ fn call 1
fn2(() => {
console.log("middlewar");
});
$ fn2 call
$ ƒ () {}
$ middlewar
즉시 실행 함수를 사용하여 $ 전역 변수의 충돌을 피할 수 있습니다.
(function ($) {
// $ 는 jQuery object
})(jQuery);
시간 측정
// timer 시작
console.time(label)
//.... 작업 구간 ....
// timer 종료
console.timeEnd(label)