06. Javascript/기초

03. 자바스크립트(JavaScript) ES6 기본

THE HEYDAZE 2020. 7. 27. 08:37
OS Windows 10 Home 64bit 버전 1903 (OS 빌드 18362.836)
JavaScript ES6 (2015年)

# 간단 설명

1. ES5 의 상위버전

2. class 지원 (+정적 메소드 지원)

3. let, const 키워드 추가

4. arrow 문법 지원

5. iterator / generator 추가

6. module import / export 추가

7. Promise 도입

8. replaceAll() 추가

 

# 온라인 에디터
 

PlayCode - Javascript Playground

Run javascript or typescript code online and see the result as you type. Best for practice code and developing complex algorithms

playcode.io

 

# 블록 스코프, let, const

자바로 얘기하자면 

var전역변수

let지역변수

const상수

 

var 와 let 의 차이
var let
i 출력 가능 i 출력 불가
country 값이 변경 되었다 country 값이 변경되지 않았다

외부 블록{} 에서 내부 블록 {} 접근은 불가능 하지만,

내부 블록{} 에서 외부 블록 {} 접근은 가능하다.

var 와 const 차이
var const
const 는 상수 이기 때문에 한 번 선언되면 값을 변경할 수 없다.

 

# 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 클래스
class User {
  // 생성자
  constructor(name, country) {
    this.name = name;
    this.country = country;
  }
  // 메소드
  information() {
    console.log('이름은 ' + this.name + ' 국가는 ' + this.country + ' 입니다.');
  }
  // getter
  get city() {
    return 'seoul';
  }
}
 
// 클래스
class Member extends User {
  // 생성자
  constructor(name, country) {
    // 부모 클래스 생성자
    super(name, country);
    //private 변수 (setter 와 getter 의 개념 사용 시 _ 붙인다.)
    this._tasks = []; 
    // 메소드
    this.greeting = function() {
      console.log('안녕하세요 ' + this.name + ' 입니다');
    }
  }
  // 메소드
  information() {
    console.log('이름은 ' + this.name + ' 국가는 ' + this.country + ' 작업량은 ' + this._tasks.length + ' 입니다.');
  }
  // 메소드
  work() {
    for(let task in this._tasks) {
      console.log(this._tasks[task]);
    }
  }
  // setter
  set tasks(task) {
    this._tasks = task;
  } 
 
  // 정적 메소드
  static maxTasksCapacity() {
    return 10;
  }
}
// 인스턴스화
var member = new Member('heydaze''korea');
 
console.log(member.name); // Member.name
console.log(member.city); // User.getter city()
member.information(); // Member.information()
member.greeting(); // Member.greeting()
member.work(); // Member.work() = []
member.tasks = ['a','b','c']; // Member.setter tasks()
member.work(); // Member.work() = for-in
console.log(Member.maxTasksCapacity()); // Member static Method
 
cs

 

크롬 콘솔

 

정적메소드(static Method)는 어디에서나 사용할 수 있다.

[클래스].[정적메소드] = Member.maxTasksCapacity();

 

[위 코드에서 메소드를 추가하기]

일반 메소드 추가 정적 메소드 추가

 

# Enhanced 리터럴

Enhanced 리터럴은 프로토타입 설정, 프로퍼티 축약, 메소드 축약,

super 호출, 표현식을 이용한 프로퍼티 계산 기능을 지원한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class User {
  constructor(name, interests) {
    this.name = name;
    this.interests = interests;
  }
  greeting () {
    console.log('Hi, I\'m ' + this.name + '.');
  }
  get interestsCount () {
    return this.interests ? this.interests.length : 0;
  }
}
 
class TeamMember extends User {
  constructor(name, interests) {
    super(name, interests);
    this._tasks = [];
    this._welcomeText = 'Welcome to the team!';
  }
  greeting () {
    console.log('I\' m ' + this.name + '. ' + this._welcomeText);
  }
  work () {
    console.log('I\' m working on ' + this._tasks.length + ' tasks.')
  }
  set tasks (tasks) {
    let acceptedTasks = [];
    if (tasks.length > TeamMember.maxTasksCapacity()) {
      acceptedTasks = tasks.slice(0, TeamMember.maxTasksCapacity());
      console.log('It\'s over max capacity. Can only take two.');
    } else {
      acceptedTasks = tasks;
    }    
    this._tasks = this._tasks.concat(acceptedTasks);
  }
  static maxTasksCapacity () {
    return 2;
  }
}
// 위 쪽 코드는 Enhanced 리터럴 코드를 설명하기 위해 작성된 코드입니다 
 
// Enhanced 리터럴
const advice = 'Stay hungry. Stay foolish.';
 
let advisor = {
  __proto__: new TeamMember('Adam', ['Consulting']), // 프로토타입 설정
  advice,                                            // 프로퍼티 축약 표현 ( advice: advice )
  greeting () {                                      // 메소드 축약 표현 (greeting: function() {} )
    super.greeting();                                // super 메소드 호출
    console.log(this.advice); 
  },
  [advice.split('.')[0]]: '계산된 프로퍼티 이름으로 호출'        // 계산된 프로퍼티 이름
};
 
console.log(TeamMember.prototype.isPrototypeOf(advisor));  // true
console.log(advisor instanceof TeamMember);                // true
advisor.greeting();   // I' m Adam. Welcome to the team!
                      // Stay hungry. Stay foolish.
console.log(advisor['Stay hungry']) // 계산된 프로퍼티 이름으로 호출
 
cs

 

크롬 콘솔

46번 줄에서 TeamMember 를 프로토타입으로 설정 (상속) 하였다.

47번 줄에서 advice 프로퍼티에 const advice 배열을 담았다.

48번 줄에서 greeting 메소드를 재정의 하였고 안에서 상위 클래스의 greeting()을 호출하였다.

52번 줄에서 프로퍼티명을 계산하여 프로퍼티명을 동적으로 사용할 수 있게 되었다.

 

# Arrow 함수

화살표 함수는 => 구문을 사용한 함수의 축약 표현이다.

화살표 함수는 본문이 표현식으로 구성된 것뿐만 아니라 명령문 블록으로 구성된 것도 지원한다. 

표현식으로 구성된 본문을 이용할 때 함수가 반환할 값은 표현식의 결과가 된다.

(자바의 람다식과 비슷)

  ES5 ES6
  1. 인자가 없을 때는 () 써야한다
  2. 하나의 인자가 있을 때
      괄호 생략가능하다

     표현식의 값은 함수의 반환 값이다
  3. 함수가 객체 리터럴을 반활 할 때
     괄호로 감싸야 한다.
  4. 화살표 함수가 구문들로 이뤄진
     본문
을 가지고 있고, 결과를 반환
     해야
할 때 return 구문 필요

 

3. 유형에서 주의사항

화살표 함수에 중괄호를 사용하려면 함수 본문은 단일 또는 여러 명령문으로 구성돼야 한다.

 

Arrow 함수는 자신의 this 를 가지지 않는다.

함수 표현식 greeting() 은 this 를 갖지만

arrow 함수를 사용한 information 은 this 를 갖고 있지 않다.

 

예제 1
ES6 ES5
ES6
ES5

ES6 의 6번 라인에서 this 는 shoppingCart 객체 자신을 참조하고 있으며, 심지어 Array.prototype.forEach() 메소드

의 콜백 내부에서도 참조하고 있다.

ES5 의 8번 라인에서 보다시피, ES5 버전에서는 실행 컨텍스트를 콜백함수에서 사용할 수 있게 하기 위해

클로저를 사용해야 한다.

 

화살표 함수는 분리된 실행 컨텍스트를 가지고 있지 않기 때문에 call() 이나 apply(), bind() 메소드를 활용해 호출하면

첫 번째 인자로 전달받은 실행 컨텍스트가 무시된다.

예제 2

 

3번 라인에서 보다시피, 화살표 함수에서 this는 항상 상위 스코프의 실행 컨텍스트로 해석된다.

call() 이나, apply(), bind() 메소드는 자체 실행 컨텍스트에 아무런 영향을 끼치지 못한다.

 

화살표 함수는 상위 스코프의 실행 컨텍스트를 이용하기 때문에 객체의 메소드를 정의하는 데 사용하는 것은 적합하지 않다.

 

예제 3

4번 라인에서 checkou을 화살표 함수로 변경한다. 화살표 함수는 자신을 둘러싸고 있는 실행 컨텍스트를

이용하므로 5번 라인의 this는 더이상 shoppingCart 객체를 참조하지 않고 TypeError 에러를 던진다.

 

예제 4

출력 결과에서 보다시피 7번 라인의 this는 User 객체를 참조하지 않는다.

이 예제에서는 전역 컨텍스트를 참조한다.

 

예제 5

화살표 함수는 프로토타입 객체를 갖지 않는다

화살표함수는 프로토 타입 객체를 가지지 않기 때문에 생성자 함수도 가지지 않는다.

그리고 new 연산자로 호출할 수 없다. 이를 시도 하면 위의 그림처럼 에러를 던진다.

 

# 매개변수 기본값

ES6 에서는 함수 매개변수의 기본값을 정의 할 수 있다.

ES5 ES6

ES6 에서 매개변수에 size=1 을 줌으로써 

매개변수값으로 들어오지 않으면 기본으로 1로 설정해준다.

 

# 나머지 매개변수

ES5 에서는 함수 본문 내에서 함수의 매개변수들을 반복하는데 arguments 객체를 이용할 수 있다.

ES6 에서는 나머지 매개변수(rest parameters) 구문으로 무한 개의 매개변수를 배열로 정의하는데 사용할 수 있다.

ES5 ES6

 

# 전개 구문

ES6 에서 3점 표기법(...)을 함수 선언 내에 사용하면 그것이 나머지 매개변수(rest parameter)를 정의 한다.

이 표기법을 배열에서 사용하면 배열의 요소들을 전개시킨다. 이 방법으로 배열의 각 요소를 함수에

전달 할 수 있다. 또 한 그것을 배열 리터럴 내에서도 사용할 수 있다.

3번 라인에서 전개 구문(spread syntax)으로 urgentTask 배열과 normalTasks 배열을 확장한다.

7번 라인에서 전개 구문으로 allTasks 배열을 확장하고 함수의 인자로 각 요소를 전달한다.

first 인자는 'Buy three tickets' 값을 가지고 second 인자는 'Book a hotel' 값을 가진다.

 

# 비구조화 할당

ES6에서는 비구조화 할당(destructuring assignment)으로 배열 내의 요소, 문자열 내의 문자, 객체의 프로퍼티를

분리하고 배열 리터럴, 객체 리터럴과 비슷한 구문을 이용해 구분된 변수들을 할당할 수 있다.

이를 변수 선언이나 변수 할당, 함수 매개변수 할당에 활용할 수 있다.

 

# 객체 비구조화

 

# 배열 비구조화

배열 비구조화(array destructuring)는 객체 비구조화와 비슷하다

배열 비구조화는 비구조화 하는데 중괄호 대신 대괄호를 이용한다.

 

또한 다음과 같이 변수를 건너뛸 수 있고, 필요한 것만 선택 할 수 있다.

보다시피 처음 변수 두 개는 건너뛰고 세 번째와 네 번째만 필요하다.

하지만 이 경우 fourth 변수가 배열내 어떤 요소와도 일치하지 않아 그 값이 undefined로 남는다.

그것을 막기위해 위 그림처럼 fourth = '' 기본값으로 설정해준다.

 

# 중첩 비구조화

복잡한 중첩 데이터 구조를 간결한 구문으로 표현하려는 데 객체 리터럴과 배열 리터럴을 활용하는 것 처럼

깊게 중첩된 데이터 구조에서 변수를 선택하는 데 비구조화 할당을 사용할 수 있다.

user 의 두 번째 관심사(interest)만 필요한 다음 예제를 보자

2번 라인에서 비구조화 할당 내에 interests가 있음에도 불구하고 자바스크립트는 사실선언하지 않는다.

4번 라인에서 보다시피 interests에 접근하면 ReferenceError가 발생한다. 여기서 일어나는 일은

자바스크립트 콜론(:)의 왼쪽 부분(여기서는 interests)을 이용해 같은 이름의 프로퍼티 값을 추출하고,

오른쪽 부분을 이용해 더 많은 비구조화 할당을 수행하는 것이다.

이전처럼 interests 프로퍼티를 추출하려면 let {interests} = user; 와 같이 작성해야 한다.

 

다음은 배열에서 두 번째 요소의 name 프로퍼티를 비구조화하는 다른 예제다.

 

# 나머지 요소

비구조화 할당에서 배열의 나머지 요소를 다른 배열에 넣는 데 나머지 요소(rest elements)와 동일한 구문을

사용 할 수 있다.

보다시피 배열의 두 번재와 세 번째 요소가 others 변수로 복사됐다. 배열을 복사하는 데 이 구문을 사용할 수 있다.

하지만 이는 얇은 복제(shallow clone) 일 뿐이다. 배열의 요소가 객체일 때 복사 된 배열에서 한 객체 프로퍼티를

변경하면 본질적으로 두 배열의 요소가 동일한 객체를 참조하고 있기 때문에 원본 배열에서도 변경된다.

 

2번 라인에서 보다시피, 비구조화 할당 구무능로 fruits 배열을 myFruits 배열로 복사한다.

그리고 4번 ~ 6번 까지 보다시피 복사된 배열에 새로운 아이템을 추가하는 것은 원본 배열에 영향을 주지 않는다.

하지만 복사된 배열에서 price 프로퍼티 값을 변경하면 원본 배열에서도 값이 변경된다.

 

# 함수 매개변수 비구조화

함수 매개변수에서도 비구조화 할당을 적용할 수 있다.

1번 라인에서 보다시피 객체 비구조화 구문으로 workout() 함수의 첫 번째 인자에서 gym 변수를 추출한다

여기서 workout() 함수에 전달된 인자는 null 또는 undefined이 되면 안 된다.

그러면 TypeError를 던지게 된다. workout() 함수에 숫자나 문자열, 배열 함수를 전달하면 gym 변수의 값이

undefined로 될 뿐 자바스크립트는 전혀 불만을 표현하지 않을 것이다.

 

비구조화된 변수를 더 비구조화하는 또 다른 예제

1번 라인에서 첫 번째 인자의 매개변수 비구조화를 수행하고 2번 라인에서 todos 변수의 추가 비구조화를 수행한다

여기서 workout() 함수에 전달되는 인자는 todos 프로퍼티를 가져야 하며 그 값은 배열이어야 한다.

그렇지 않으면 7번 라인처럼 TypeError 를 던진다. 이는 2번 라인에서 자바스크립트가 undefined 또는 null 에 대해서

비구조화를 수행할 수 없기 때문이다. 다음과 같이 todos에 기본값을 설정해 이 문제를 개선할 수 있다.

1번 라인에서 보듯이 todos 변수에 기본값만 주고 workout 함수를 단일 매개변수로 호출하게 했다.

6번 라인처럼 어떤 매개변수도 없이 호출하면 여전히 에러를 던진다. 이는 자바스크립트가 여전히

gym 변수에 대한 값을 가져오기 위해 undefined에 대한 비구조화를 수행할 수 없기 때문이다.

그리고 workout({gym='',...) 와 같이 gym 변수에 기본값을 할당하더라도 동작하지 않는다.

다음과 같이 전체 매개변수 비구조화에 기본값을 할당해야 한다.

 

구조 분해 할당
얕은 복사로 인해 구조분해 할당이 가능한 상태
function solution(arr) {

  [arr[0], arr[1]] = [arr[1], arr[0]]

  console.log(arr)

}

let arr = [20,10]
solution(arr)

 

깊은 복사로 인해 init error 발생
let arr = [20,10]
[arr[0], arr[1]] = [arr[1], arr[0]]

 

템플릿 리터럴

템플릿 리터럴은 문자열 리터럴에 표현식을 포함하고 여러 라인을 지원하는 기능을 제공한다.

이 구문은 문자열을 묶는 데 작은따옴표나 큰따옴표 대신 역 따옴표(`) 문자를 사용한다.

템플릿 리터럴 내부에서 확인 할 수 있듯이 ${...} 구문을 사용해 this로 해당 실행 컨텍스트에 접근 할 수 있다.

여기서 한 가지 주의할 점은 역 따옴표 문자 내부에 있는 모든 공백도 출력된다는 점이다.

따라서 두 번째 줄에서 공백삽입시 출력 결과가 보기에 좋지 않다. (아래 그림 참고)

# 모듈

ES6에서 자바스크립트는 언어 차원에서 모듈(modules)을 지원한다. 모듈을 구성하고 정적 모듈 구조를 만드는 데

export 와 import 를 사용한다. 즉, 컴파일 시점에서 가져오기(import)와 내보내기(export)를 결정 할 수 있다.

ES6 모듈의 또 다른 중요한 특징은 가져오기와 내보내기에 대한 선언이 최상위에 위치해야 한다는 것이다.

그것들은 if와 try/catch 구문과 같은 블록 내에 넣을 수가 없다.

 

모듈을 생성하기 위해 해야 할 일은 자바스크립트 코드를 .js 파일에 넣는 것 뿐이다.

바벨(http://babeljs.io)과 같은 도구를 사용해 ES6 코드를 ES5 코드로 컴파일하거나 웹팩과 같은 도구를 함께

사용해 코드를 묶는 방법을 선택할 수 있다. 모듈 파일을 이용하는 또 다른 방법은

<script type="module"> 으로 모듈파일을 브라우저로 불러오는 것이다.

 

user.js
1
2
3
4
5
6
7
export default class User {
  constructor (name, role) {
    this.name = name;
    this.role = role;
  }
};
 
cs

export default 는 기본 내보내기로 User 클래스를 내보낸다

 

role.js
1
2
3
4
const DEFAULT_ROLE = 'User';
const ADMIN = 'Admin';
export {DEFAULT_ROLE as USER, ADMIN};
 
cs

이 모듈에서 두 개의 상수를 생성한 다음 이들을 중괄호로 감싸 명명된 내보내기로 내보낸다(export)

중괄호를 감쌋다고 해서 객체를 내보낸 것으로 생각하면 안 된다. 

그리고 3번 라인에서 보다시피 export 에서 이름을 변경할 수 있다. 또한 import 에서도 이름을 변경할 수 있다.

(DEFAULT_ROLE 에서 USER 로 이름 변경됨)

 

task.js
1
2
3
4
5
6
7
8
console.log('Inside tasks module');
export default function completeTask(user) {
  console.log(`${user.name} completed a task`);
  completedCount++;
}
// 완료된 작업의 개수를 
export let completedCount = 0;
 
cs

이 모듈에서 2번 라인에서는 compleTask 함수를 기본 내보내기하고,

6번 라인에서는 completedCount 변수에 대해 명명된 내보내기를 했다.

 

app.js (모듈)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import User from './user.js';    
import * as Roles from './roles.js';
import completeTask from './tasks.js';
import {completedCount} from './tasks.js';
 
let user = new User('Ted', Roles.USER);        
completeTask(user);    
console.log(`Total completed ${completedCount}`);
// completedCount++;  
// 가져온 객체를 변경할 수 있다는 것만 보여준다
// 하지만 좋은 사례는 아니다.
User.prototype.walk = function () {
  console.log(`${this.name} walks`);
};
user.walk();
 
cs

 

1번 라인에서 기본 가져오기(default import)로 user.js 모듈에서 User 클래스를 가져온다.

예를들어 import AppUser from './user.js' 와 같이 User 이외에 다른 이름을 사용할 수 있다. 기본 가져오기는 

기본 내보내기(default export)에서 사용한 이름과 일치하지 않아도 된다.

 

2번 라인에서는 role.js 모듈을 가져와서 Roles로 이름을 지정하는 네임스페이스 가져오기(namespace import)를 사용

한다. 그리고 6번 라인에서 보듯이 점 표기법으로 명명된 내보내기를 한 role.js 모듈에 접근한다.

 

3번 라인에서는 기본 가져오기로 task.js 모듈에서 completeTask 함수를 가져온다.

그리고 4번 라인에서 명명된 가져오기로 다시 같은 모듈에서 completedCount 를 가져온다. ES6 모듈은

싱글톤(Singletone)이기 때문에 여기에서처럼 두 번 가져오기를 해도 task.js 모듈의 코드를 한 번만 평가한다.

이것을 실행했을 때 단 한 번의 Inside tasks module이 출력되는 것을 확인할 수 있다. 

기본 가져오기(default import)와 명명된 가져오기(named import)를 함께 작성할 수 있다. 

다음은 앞의 3번 및 4번 라인과 같은 코드 이다.

1
2
import completeTask, {completeCount} from './tasks.js';
 
cs

 

모듈에서 다른 로컬 이름과 충돌한다면 명명된 가져오기의 이름을 변경할 수 있다.

1
2
import {completedCount as totalCompletedTasks} from './tasks.js';
 
cs

 

함수 선언과 마찬가지로 가져오기는 호이스팅이 된다. 따라서 6번 라인 이후에 1번 라인을 넣어도 여전히 동작한다.

하지만 이 방법은 가져오기를 구성하는 권장 방법은 아니다. 의존관계를 한눈에 볼 수 있도록 모듈의 상단에

모든 가져오기 코드를 작성하는 것이 좋다.

1
2
3
4
// 호이스팅으로 인해 정상적으로 작동하지만, 권장하는 방법이 아니다.
let user = new User('Ted', Roles.USER);
import User from './user.js';
 
cs

 

app.js 모듈 설명

 

HTML
1
2
3
4
5
6
7
8
<!DOCTYPE html>
<html>
<body>
  <script type="module" src="./app.js"></script>
  <script>console.log('A embedded script');</script>
</body>
</html>
 
cs

<script type="module"> 은 HTML 내에 작성하며 기본으로 defer 속성을 가진다.

defer 란 브라우저가 DOM 구문 분석을 마친 후 모듈을 실행 한다는 것을 의미한다.

즉 body 내용을 전부 로드한 후에 app.js 안의 import 부터 시작한다 (jQuery 의 document.ready 와 같음)

app.js -> index.html -> user.js -> roles.js -> tasks.js 순으로 불러와진다 (아래 그림 참고)

 

NGINX와 같은 HTTP 서버에서 이것을 실행해야 한다. CORS 정책으로 크롬해서 index.html 을 파일로 직접 여는 방법은 동작하지 않는다. (좋은 edit Tool 은 실행됩니다 - WebStorm 등등..)

 

CORS(Cross-Origin-Resource Sharing) = 교차 출처 자원 공유:

웹 페이지상의 제한된 리소스를 최초 자원이 서비스된도메인 밖의 다른 도메인으로부터 요청할 수 있게 허용하는 구조를 말한다

 

ES6부터 자바스크립트에는 스크립트(script)와 모듈(modules)의 두 가지 형태가 있다.

strict 모드로 코드를 렌더링하기 위해 파일의 최상위에 'use strict';를 넣어야 하는 스크립트 코드와 달리 모듈 코드는

자동으로 strict 모드 상태이다. 그리고 모듈의 최상위 변수는 외부에서 활용하기 위해 내보내기 하지 않는

해당 모듈의 로컬 변수이다. 그리고 모듈의 최상위에서 this 는 undefined를 가르킨다.

브라우저에서는 모듈 내부의 window 객체에 여전히 접근 할 수 있다.

 

# 프로미스
콜백함수

: 비동기 호출에서 사용되는 익명함수

 

[JS] 콜백 함수 (Callback Function)

2019. 06. 02 수정 1. 콜백함수란? JS에서 콜백 함수는 너무나 중요한 개념입니다. Node.js 환경에서 프로그래밍 시 반드시 필요한 개념이기도 합니다. 웹 개발을 해보셨으면 jQuery를 한 번 쯤은 사용��

victorydntmd.tistory.com

 

 

[JavaScript] 자바스크립트 콜백(callback)함수란?

콜백 함수란?  콜백 함수는 함수 안에서 어떤 특정한 시점에 호출되는 함수를 말합니다. 보통 콜백 함수는 함수의 매개변수로 전달하여 특정 시점에서 콜백 함수를 호출합니다. 그럼 콜백 함수�

webcoding.tistory.com

 

callback

 

21번부터 25번라인에서 보다시피 비동기 호출을 구성하는 데 콜백을 활용한다.

여기에 있는 코드가 굉장히 단순한 형태임에도 불구하고, getProjects() 와 getTasks(), render() 메서드가 여전히 

중첩되어 파멸의 피라미드 혹은 콜백 지옥(callback hell)이 생성된 것을 볼 수 있다.

아래의 프로미스를 활용한 버전을 확인해보자

 

promise

1번부터 15번라인의 getProjects()와 getTasks() 메소드에서 비동기 연산을 래핑한 Promise 객체를 바로 반환한다

Promise 생성자 함수는 매개변수로 하나의 함수를 가진다. 이 함수를 실행함수(executor function)라 부르며,

rosolve 함수와 reject 함수가 인수로 전달되어야 실행된다. 이 두 함수는 Promise 구현체에서 제공한다.

비동기 작업이 완료되면 작업 결과 또는 결과가 없는 형태로 resolve를 호출한다. 그리고 작업이 실패하면

reject 함수를 이용해 프로미스를 거부할 수 있다. 실행 함수 내에서 에러가 던져질 때도 프로미스는 거부된다.

  • 대기(Pending): 프로미스의 초기상태
  • 이행(Fulfilled): 작업을 성공적으로 완료했을 때 상태
  • 실패(Rejected): 에러 또는 기타 이유로 작업을 성공적으로 완료하지 못했을 때의 상태

프로미스의 상태를 프로그래밍으로 얻을 수는 없다. 대신에 프로미스의 상태가 이행으로 변경될 때

행동을 취하는 데 .then() 메소드를 활용할 수 있고, 상태가 실패로 변경되거나 작업 중에 오류가 발생할 때

반응하기 위해 catch 메소드를 활용할 수 있다. promise 객체의 .then() 메소드는 매개변수로 두 개의 함수를 가진다.

인자에서 첫 번째 함수는 프로미스가 이행됐을 때 호출되고, 일반적으로 onFulfilled로 참조되며

두 번째 함수는 프로미스가 거부됐을 때 호출되고, 일반적으로 onRejected로 참조된다.

또한 .then() 메소드는 promise 객체를 반환한다. 21번부터 26번까지 라인에서 보다시피 모든 작업을

연결하는 데 .then() 메소드를 활용할 수 있다. 22번 라인의 .catch() 메소드는 실제로 .then(undefined, onRejected)의

설탕 구문이다. 여기서 .catch() 메소드는 모든 거절과 에러를 잡아내기 위해 체인의 마지막에 넣는다 .catch() 이후에

추가작업을 수행하기 위해 .then() 메소드를 추가할 수 있다.

 

 

ES6의 Promise는 다중 프로미스의 결과를 집계하는 .all(iterable) 메소드와 iterable 내의 프로미스 중

하나가 이행되거나 거부되는 즉시 이행 또는 거부하는 프로미스를 반환하는 .race(iterable) 메소드도 제공한다.

ES6의 Promise가 제공하는 또 다른 두 개의 메소드에는 .resolve(value) 메소드와 .reject(reason) 메소드가 있다.

.resolve(value) 메소드는 Promise 객체를 반환한다. value가 프로미스이면 반환된 프로미스는 최종 상태를

채택한다. 즉 반환된 프로미스의 .then() 메소드를 호출하면 onFulfilled 핸들러는 value 프로미스의 결과를 가져온다

value 가 프로미스가 아니라면 반환된 프로미스는 이행 상태고 그 결과가 value가 된다.

reject(reason) 메소드는 거부된 이유를 의미하는 reason과 함께 거부 상태의 프로미스를 반환한다.

 

replaceAll()

기존에 es5 에서는 replace() 밖에 존재하지 않아 모든 문자를 변경하고자 하려면 정규식을 활용해야 했다

var s = 'Java 와 JavaScript'

console.log(s.replace(/Java/g, '자바'))

 

하지만 ES6 문법에 replaceAll() 이 추가되었다

var s = 'Java 와 JavaScript'

console.log(s.replaceAll('Java', '자바'))