Strong Root


프라미스 (Promise) 란


비동기적으로 결과를 리턴하는 함수에 대한 비동기 프로그래밍 패턴의 일종이다.


(※ 매우 중요한 개념이므로 필히 익히시기를 추천합니다.)






A부터 Z까지 Promises 의 모든 것


http://www.html5rocks.com/ko/tutorials/es6/promises/


너무도 완벽한 글을 찾아서 본문 없이 그냥 위 링크로 대체합니다.




한편, 퀄리티가 이 정도로 좋은 글을 쓰는 사람은 어떤 사람인지 궁금해서 찾아보았습니다.



네... 그렇습니다. 구글러네요.


전공적인 깊은 이해는 물론이고 글솜씨와 전개방식도 완벽합니다.


존경합니다.



프록시 (Proxy) 란


Proxy 의 사전적 의미는 "대행", "대리", "위임" 이다.


프록시 (Proxy) 는 객체에 수행되는 동작들 (예를들면 속성값 조회, 변경) 을 가로챌 수 있게 해주고, 커스터마이징도 할 수 있게 해주는 일종의 객체 감싸미다.


프록시는 세 요소로 구성된다:



 * 타겟 (target) : 프록시로 감쌀 대상 객체.


 * 핸들러 (handler) : 트랩 메서드들을 담고 있는 객체. 타겟에 대한 동작을 감지하여 그에 대응하는 트랩 메서드가 존재할 경우 해당 트랩 메서드를 호출한다.


 * 트랩 (trap) : 핸들러 안에 존재하는 메서드. 타겟 객체에 대한 동작을 가로채며, 사용자 정의 로직을 넣을 수 있다.





아래는 프록시를 적용한 예제 코드이다. 여기서는 트랩으로 get, deleteProperty 가 사용되었다.


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
var target = {a: 1};
 
var handler = {
  get: function(target, key) {
    // any custom logic
    console.log('GET: ' + key);
    return target[key];
  },
 
  deleteProperty: function(target, key) {
    // any custom logic
    console.log('DELETE: ' + key);
    return delete target[key];
  }
};
 
var proxiedTarget = new Proxy(target, handler);
 
console.log(proxiedTarget.a);
// GET: a
// 1
 
console.log(proxiedTarget.b);
// GET: b
// undefined
 
delete proxiedTarget.a;
// DELETE: a
cs











Reflect


ECMAScript 6 에서는 모든 트랩에 대하여 포워딩을 도와주는 Reflect 라는 객체가 추가되었다:



 handler.trap(target, arg_1, ···, arg_n)


 위와 같은 모든 trap 메서드에 대응하는


 Reflect.trap(target, arg_1, ···, arg_n)


 이 존재한다.





Reflect 를 이용한 포워딩 (Forwarding intercepted operations with Reflect):


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
var target = {a: 1};
 
var handler = {
  get: function(target, key) {
    // any custom logic
    console.log('GET: ' + key);
    return Reflect.get(target, key);
  },
 
  deleteProperty: function(target, key) {
    // any custom logic
    console.log('DELETE: ' + key);
    return Reflect.deleteProperty(target, key);
  }
};
 
var proxiedTarget = new Proxy(target, handler);
 
console.log(proxiedTarget.a);
// GET: a
// 1
 
console.log(proxiedTarget.b);
// GET: b
// undefined
 
delete proxiedTarget.a;
// DELETE: a
cs











Why Proxy? Proxy 의 강력한 활용


아래 코드를 이해하면 Proxy 의 강력함을 느낄 수 있다.


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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
let normalObj = {name'CGun'};
let dfObj = makeDefensiveObject(normalObj);
 
/* [1] name */
console.log(dfObj.name);
// GET: name
// CGun
 
 
 
/* [2] marriage */
console.log(dfObj.marriage);
// GET: marriage
// Uncaught ReferenceError: marriage does not exist.
 
dfObj.marriage = 'x';
// SET: marriage, x
// Uncaught TypeError: marriage should be "기혼" or "미혼"
 
dfObj.marriage = '미혼';
// SET: marriage, 미혼
 
console.log(dfObj.marriage);
// GET: marriage
// 미혼
 
 
 
/* [3] age */
console.log(dfObj.age);
// GET: age
// Uncaught ReferenceError: age does not exist.
 
dfObj.age = 'x';
// SET: age, x
// Uncaught TypeError: The age is not an integer
 
dfObj.age = -30;
// SET: age, -30
// Uncaught RangeError: The age seems invalid
 
dfObj.age = 29;
// SET: age, 29
 
console.log(dfObj.age);
// GET: age
// 29
 
 
 
/* [4] grade */
console.log(dfObj.grade);
// GET: grade
// Uncaught ReferenceError: grade does not exist.
 
dfObj.grade = 80;
// SET: grade, 80
// Uncaught TypeError: grade should be "A" to "F"
 
dfObj.grade = 'b';
// SET: grade, b
 
console.log(dfObj.grade);
// GET: grade
// B
 
 
 
/* [5] Proxy */
function makeDefensiveObject(target) {
    if (target === undefined) {
        target = {};
    }
 
    let handler = {
        get: function(target, key) {
            console.log('GET: ' + key);
 
            if (!(key in target)) {
                throw new ReferenceError(key + ' does not exist.');
            }
 
            if (key == 'marriage') {
                return Reflect.get(target, key) === true ? '기혼' : '미혼';
            }
 
            return Reflect.get(target, key);
        },
        set: function(target, key, value) {
            console.log('SET: ' + key + ', ' + value);
 
            if (key == 'marriage') {
                if (value == '기혼') {
                    value = true;
                }
                else if (value == '미혼') {
                    value = false;
                }
                else {
                    throw new TypeError('marriage should be "기혼" or "미혼"');
                }
            }
 
            if (key == 'age') {
                if (!Number.isInteger(value)) {
                    throw new TypeError('The age is not an integer');
                }
                if (value < 0 || value > 150) {
                    throw new RangeError('The age seems invalid');
                }
            }
 
            if (key == 'grade') {
                if (!value || value.length != 1) {
                    throw new TypeError('grade should be "A" to "F"');
                }
 
                value = value.toUpperCase();
                if (value < 'A' || value > 'F') {
                    throw new RangeError('The grade seems invalid');
                }
            }
 
            return Reflect.set(target, key, value);
        }
    };
 
    return new Proxy(target, handler);
}
cs











Revocable proxy


ECMAScript 6 에서는 취소가 가능한 (스위치를 끌 수 있는) 프록시가 추가되었다:


1
var {proxy, revoke} = Proxy.revocable(target, handler);
cs




revoke 함수를 실행하고 나면 해당 proxy 가 취소되며, 사용하려고 하면 TypeError 가 발생한다:


1
2
3
4
5
6
7
8
9
10
var target = {}; // Start with an empty object
var handler = {}; // Don’t intercept anything
var {proxy, revoke} = Proxy.revocable(target, handler);
 
proxy.foo = 123;
console.log(proxy.foo); // 123
 
revoke();
 
console.log(proxy.foo); // TypeError: Revoked
cs











출처


http://exploringjs.com/es6/ch_proxies.html

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy




  한 collection 에서 각각의 아이템을 순회하는 것은 매우 흔한 작업이다.


  Iterator 와 Generator 는 iteration 에 대한 직접적인 적용 및 커스터마이징 메커니즘을 제공한다.







ES6 에서의 Iterator (Iterator in ES6)


Iterator 는 새로운 문법이나 built-in 이 아니라 프로토콜(약속)이다.


이 프로토콜은 몇 가지 규칙만 준수한다면 어떠한 객체에서도 구현될 수 있다.











Iterator 프로토콜 (The iterator protocol)


Iterable 한 객체가 되려면 Symbol.iterator 를 Key 로 갖는 속성이 반드시 존재해야 한다:


Property

Value

[Symbol.iterator]

  Iterator 프로토콜을 준수하는 객체를 리턴하며 인자가 없는 함수




다음 규칙에 따라 next() 메서드를 구현한 객체를 iterator 라고 한다:


Property

Value

next

 아래의 두 속성을 가지는 객체를 리턴하며 인자가 없는 함수:


 1. done (boolean)

     * iterator 가 순회를 모두 마쳤을 경우 true

     * iterator 가 순회할 다음 value 가 존재할 경우 false

 2. value - iterator 에 의해 리턴될 값. done 이 true 일 경우 생략 가능




Iterable 하며 Iterator 프로토콜을 만족하는 예시 코드:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var someObject = {};
 
...
 
someObject[Symbol.iterator] = function () {
  var num = 1;
 
  return {
    next: function() {
      return num < 4 ?
        {value: num++, done: false} :
        {done: true};
    }
  };
};
 
...
 
for (let value of someObject) {
  console.log(value);
}
// 1
// 2
// 3
cs











Generators


커스텀 iterator 는 유용하지만, 그 내부 상태를 유지하기 위한 매우 세심한 관리를 필요로 한다.


Generator 는 이에 대한 강력한 대안이다: 내부 상태를 스스로 유지하는 하나의 함수만 작성하면 된다.



Generator 는 iterator 를 위한 factory 함수의 일종이다.


하나 이상의 yield expression 을 가지며 function* 문법을 사용하는 함수를 Generator 라고 한다.



Generator 예시 코드:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var someObject = {};
 
...
 
someObject[Symbol.iterator] = function* () {
  var num = 1;
 
  while (num < 4) {
    yield num++;
  }
};
 
...
 
for (let value of someObject) {
  console.log(value);
}
// 1
// 2
// 3
cs











특이한 예제 코드 (Examples)


무한 iterator (infinite iterator):


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function idMaker(){
    var index = 0;
    
    return {
       next: function(){
           return {value: index++, done: false};
       }
    };
}
 
var it = idMaker();
 
console.log(it.next().value); // '0'
console.log(it.next().value); // '1'
console.log(it.next().value); // '2'
// ...
cs




Generator 를 사용한 무한 iterator (Infinite iterator with a generator):


1
2
3
4
5
6
7
8
9
10
11
12
function* idMaker(){
    var index = 0;
    while(true)
        yield index++;
}
 
var gen = idMaker();
 
console.log(gen.next().value); // '0'
console.log(gen.next().value); // '1'
console.log(gen.next().value); // '2'
// ...
cs











출처


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators