ECMAScript 6의 새로운 기능 정리
전통적인 함수작성 방법 대신 축약해서 표현할 수 있습니다.
콜백 함수로 활용합니다..
화살표 함수에서의 this는 감싸고 있는 코드의 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
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));
}
}
// Destructuring
// Array
var [a, , b] = [1, 2, 3];
console.log(a, b); // 1 3
// Object
var person = { name: "hanjin", age: 20, sex: "Male" };
var { name, age } = person;
console.log(name, age); // 'hanjin' 20
// person안의 name과 age값을 가져와서 다른 변수명으로 선언하기
var { name: myName, age: myAge } = person;
console.log(myName, myAge);
function g({name: x}) {
console.log(x);
}
g({name: 5}) // 5
document
.querySelector("div")
.addEventListener("click", function({ target, type }) {
// 인자 전달 시 target이나 type만 뽑아낼 수 있다.
console.log(target.tagName, type);
});
var [a] = [];
a === undefined;
var [a = 1] = [];
a === 1;
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
let data = ["godori", "irodog", "roodig"];
let newData = [...data];
console.log(newData); // ["godori", "irodog", "roodig"]
function sum(a,b,c){
return a+b+c;
}
let arr = [100,200,300];
console.log( sum.apply(null, arr) ); // 인자를 배열 형태로 받아 출력
console.log( sum(...arr) ); // 이렇게 써도 동일한 결과 출력
function f(x, ...y) {
// y is an Array
// y = ['hello', true]
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15
기존에는 문자열을 ''(single quote) 한 쌍이나 ""(double quote) 한 쌍으로 표현했는데
이제는 `(backtick) 한 쌍으로도 표현할 수 있다.
장점.
1. 줄바꿈을 위해 \n을 입력할 필요가 없다
2. 문자열 내부에 변수를 같이 사용할 수 있다.
ex)
const multiline = `This is multiline text.
Template literals don't need \n to go next line`
console.log(multiline);
//
//"This is multiline text.
//Template literals don't need \n to go next line"
const keyword = 'variable'
const withVariable = `now u can use ${keyword} in string`
console.log(withVariable);
// 'now u can use variable in string'
arr도 key가 숫자인 object라고 생각하였을때,
value기준으로 순회가 가능한 iterator
const arr = ['a', 'b', 'c'];
for(let value of arr){
console.log(value); // 'a', 'b', 'c'
}
const obj = {a:1, b:2, c:3};
for(let value of obj){
console.log(value); // 1, 2, 3
}
유사배열을 실제 배열로 만들어줌. DOM의 NodeList등이 유사배열에 포함된다
const nodeList = document.getElementsByClassName(className);
nodeList.map(callback) // Error!
const nodeListAsArray = Array.from(nodeList);
nodeListAsArray.map(callback) // It works
ES5까지는 변수는 var
로 선언하였지만, let
과 const
가 추가되었다.
변수 선언 규칙
1. const
를 기본으로 사용.
2. 변경이 될 수 있는 변수는 let
을 사용.
3. var
는 사용하지 않는다.
const
를 사용해 객체나 배열등의 참조타입을 선언할 경우
재할당이 불가능한 것이고 immutable한 값은 아니다.
es6 모듈 시스템을 사용하지 않을 경우,HTML에 로드 된 자바스크립트는 하나의 전역을 공유한다.
<!DOCTYPE html>
<html>
<body>
<script src="foo.js"></script>
<script src="bar.js"></script>
</body>
</html>
이렇게 하였을 경우, 스코프를 공유하기 때문에 아래 작성한 코드와 같은 동작을 한다.
// foo.js
var x = 'foo';
// bar.js
var x = 'bar';
console.log(x) // bar
console.log(window.x); // bar
foo에서 선언한 변수는 덮어씌워져서 사용할 수 없게 되었다.
이러한 문제점을 해결하기 위해 es6 모듈은 파일 자체의 스코프를 제공한다.
즉 모듈내에서 var 키워드로 선언한 변수도 더 이상 전역변수가 아니다.
또한 모듈내에서 선언한 변수는 모듈 외부에서 참조할 수 없다. 스코프가 다르기 때문에
<!DOCTYPE html>
<html>
<body>
<script type="module" src="foo.mjs"></script>
<script type="module" src="bar.mjs"></script>
</body>
</html>
모듈 안에 선언된 식별자, 값 등을 모듈 외부에서 사용하고 싶다면 export 키워드를 사용한다.
변수, 함수, 클래스 모두 export가 가능하다.
// lib.mjs
// 변수
export const pi = Math.PI;
// 함수
export function square(x) {
return x * x;
}
// 클래스
export class Person {
constructor(name){
this.name = name;
}
}
혹은 아래와 같은 형태로도 가능하다.
// lib.mjs
const pi = Math.PI;
function square(x) {
return x * x;
}
class Person {
constructor(name) {
this.name = name;
}
}
// 변수, 함수 클래스를 하나의 객체로 구성하여 공개
export { pi, square, Person };
모듈에서 하나만을 export할 때는 default 키워드를 사용할 수 있다.
// lib.mjs
export default function (x) {
return x * x;
}
default를 사용하는 경우, var, let, const는 사용할 수 없다.
// lib.mjs
export default () => {};
// => OK
export default const foo = () => {};
// => SyntaxError: Unexpected token 'const'
모듈에서 export한 대상을 로드하려면 import 키워드를 사용한다.
export한 식별자로 import하며 es6 모듈의 확장자를 생략할 수 없다.
// app.mjs
// 같은 폴더 내의 lib.mjs 모듈을 로드.
// lib.mjs 모듈이 export한 식별자로 import한다.
import { pi, square, Person } from './lib.mjs';
console.log(pi); // 3.141592653589793
console.log(square(10)); // 100
console.log(new Person('Lee')); // Person { name: 'Lee' }
각각의 이름이 아닌 하나의 이름으로 한꺼번에 import.
// app.mjs
import * as lib from './lib.mjs';
console.log(lib.pi); // 3.141592653589793
console.log(lib.square(10)); // 100
console.log(new lib.Person('Lee')); // Person { name: 'Lee' }
이름을 변경하여 import.
// app.mjs
import { pi as PI, square as sq, Person as P } from './lib.mjs';
console.log(PI); // 3.141592653589793
console.log(sq(2)); // 4
console.log(new P('Kim')); // Person { name: 'Kim' }
default로 export한 모듈은 임의의 이름으로 import.
// lib.mjs
export default function (x) {
return x * x;
}
// app.mjs
import square from './lib.mjs';
console.log(square(3)); // 9
자바스크립트는 전통적으로 비동기처리를 위해서 콜백 패턴을 사용해왔지만, 가독성이 나쁘고 비동기 처리 중 발생한 에러의 예외처리가 곤란하여, 비동기 처리를 위한 새로운 패턴으로 Promise가 도입되었다.
비동기처리를 위해 콜백 패턴을 사용하다보면 콜백 함수가 여러개 중첩되어 콜백 지옥이라 불리는 구조가 만들어진다. 이는 가독성이 떨어지며 실수를 유발하는 원인이 된다.
step1(function(value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
step5(value4, function(value5) {
// value5를 사용하는 처리
});
});
});
});
});
프로미스는 Promise 생성자 함수를 이용하여 인스턴스화한다.
쉽게말해서 new Promise로 선언한다는 뜻이다.
Promise 생성자 함수는 콜백함수를 인자로 받고, 이 콜백함수는 resolve와 reject함수를 인자로 받는다.
// Promise 객체의 생성
const promise = new Promise((resolve, reject) => {
// 비동기 작업을 수행한다.
if (/* 비동기 작업 수행 성공 */) {
resolve('result');
}
else { /* 비동기 작업 수행 실패 */
reject('failure reason');
}
});
Promise는 비동기 처리가 성공했는지 실패했는지 혹은 아직 수행되지 않았는지 등의 상태 정보를 갖는다.
상태 | 의미 | 구현 |
---|---|---|
pending | 비동기 처리가 아직 수행되지 않은 상태 | resolve 또는 reject 함수가 아직 호출되지 않은 상태 |
fulfilled | 비동기 처리가 수행 된 상태(성공) | resolve 함수가 호출 된 상태 |
rejected | 비동기 처리가 수행 된 상태(실패) | reject 함수가 호출 된 상태 |
settled | 비동기 처리가 수행 된 상태(성공 또는 실패) | resolve 또는 reject 함수가 호출 된 상태 |
Promise 로 구현된 비동기 함수는 Promise 객체를 반환하여야 한다.
상태에 따라 후속 처리 메서드를 체이닝 방식으로 호출한다.
then
then 메소드는 두 개의 콜백함수를 전달받는다. 첫번째 콜백함수는 fulfiled시 호출되고 두번째 콜백함수는 rejected시 호출된다.
then 메소드는 Promise를 반환한다.
catch
예외가 발생하면 호출된다. catch 메소드는 Promise를 반환한다.
then 메소드의 두번째 콜백함수를 이용해서 도 에러처리가 가능하지만, 비동기 처리에서 발생한 에러만 캐치한다. 하지만 catch 메소드는 then 메소드 내부에서 발생한 에러도 캐치하기때문에,
에러처리는 catch 메소드를 사용하는 편이 효율적이다.
프로미스를 사용하면, 콜백구조를 사용했을 때처럼 콜백함수가 중첩되며 복잡도가 높아지는 대신, 프로미스를 이어붙여나가는 패턴으로 비동기 코드를 훨씬 가독성있게 작성할 수 있다.
promiseAsync()
.then(res => promiseAsync2())
.then(JSON.parse)
.then(render)
.catch(console.error);
두 메소드는 이미 존재하는 값을 Promise로 wrapping하기 위해 사용한다.
Promise.resolve는 인자로 전달된 값을 resolve하는 Promise를 생성한다.
const resolvedPromise = Promise.resolve([1, 2, 3]);
resolvedPromise.then(console.log); // [ 1, 2, 3 ]
Promise.reject는 인자로 전달 된 값을 reject하는 Promise를 생성한다.
const rejectedPromise = Promise.reject(new Error('Error!'));
rejectedPromise.catch(console.log); // Error: Error!
Promise가 담겨있는 iterable을 인자로 전달 받는다. 전달받은 모든 Promise를 병렬로 처리하고 그 처리 결과를 resolve하는 새로운 Promise를 반환한다.
그냥 Promise 여러개를 한꺼번에 처리한다고 생각하면 된다.
예를 들어,
Promise.all([promise1, promise2, promise3])
과 같은 구조로 실행했을 때, promise1이 가장 마지막으로 resolve되어도 여전히[promise1, promise2, promise3]
의 순서로 존재한다.
const githubIds = ['jeresig', 'ahejlsberg', 'ungmo2'];
Promise.all(githubIds.map(id => fetch(`https://api.github.com/users/${id}`)))
// [Response, Response, Response] => Promise
.then(responses => Promise.all(responses.map(res => res.json())))
// [user, user, user] => Promise
.then(users => users.map(user => user.name))
// [ 'John Resig', 'Anders Hejlsberg', 'Ungmo Lee' ]
.then(console.log)
.catch(console.log);
Promise.all과 같이 Promise가 담겨있는 iterable을 인자로 전달 받는다.
Promise.race는 모든 프로미스를 병렬 처리하는 것이 아니라, 가장 먼저 처리 된 Promise가 resolve한 처리 결과를 resolve하는 새로운 프로미스를 반환한다.
Promise.race([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(console.log) // 3
.catch(console.log);
에러가 발생한 경우는 Promise.all과 같은 방식으로 처리된다.
앞에서 일치하는 문자열이 있는지 판단 : Return type Boolean
const str = 'Love never felt so good';
str.startsWith('Love'); // true
뒤에서 일치하는 문자열이 있는지 판단 : Return type Boolean
const str = 'Love never felt so good';
str.endsWith('good'); // true
문자열이 포함되어 있는지 판단 : Return type Boolean
const str = 'Love never felt so good';
str.includes('so'); // true