Strong Root


프라미스 (Promise) 란


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


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






A부터 Z까지 Promises 의 모든 것


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


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




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



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


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


존경합니다.




 우리는 구형 브라우저가 HTML5 를 올바르게 다룰 수 있도록 지도할 수 있다. 심지어 Windows XP 2001 의 IE6 도 가능하다.







최종 해결책


1
2
3
<!--[if lt IE 9]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
cs


위 세 줄을 <head> 영역에 써주면 된다.









원리1. CSS 를 통한 대응


예를 들어 구형 브라우저 입장에서 Unknown Elements 인 HTML5 Elements 에 대하여 아래와 같이 대응할 수 있다.


1
2
3
header, section, footer, aside, nav, main, article, figure {
    display: block; 
}
cs






원리2. 새로운 사용자 정의 Element 추가하기


같은 원리로 새로운 Element 를 HTML 에 추가할 수도 있다:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
  <title>New element</title>
  <script>document.createElement("myHero")</script>
  <style>
  myHero {
      display:block;
      background-color:#ddd;
      padding: 50px;
      font-size: 30px;
  }  
  </style>
</head>
<body>
 
<myHero>My First Hero</myHero>
 
</body>
cs






원리3. 인터넷 익스플로러 (Internet Explorer) 의 문제점


위의 원리2 의 해결책을 통해 모든 HTML5 Elements 에 대응할 수 있지만,


IE8 이하에서는 unknown elements 에 대한 css 적용을 허용하지 않는 문제점이 있다.


이것까지 해결한 것이 "the shiv" 라는 js 파일이다:


1
2
3
<!--[if lt IE 9]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
cs


이 코드는 주석이지만 IE9 미만의 버전에서는 단순 주석이 아니라 실제 코드로써 동작한다.









출처


http://www.w3schools.com/html/html5_browsers.asp




프록시 (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


1
2
3
4
5
6
7
8
9
var obj = document.getElementById("foo");
 
obj.value = "hi";
 
...
 
obj.value = "hello";
 
...
cs




Android 의 ViewHolder 와 거의 같은 개념이다.


DOM 을 뒤져서 get Element 하는 코드는 매우 느리기 때문에, (Android 에서는 findViewById)


해당 element 를 다시 참조할 일이 있다고 예상되면


다시 찾지말고 기존에 찾았던 것을 변수에 저장해놓고 다시 이용한다.






출처 : http://www.w3schools.com/js/js_performance.asp






ps. 드디어 JS Tutorial 정주행 완료! ^^

Loose Comparison (==)

1
2
3
var x = 10;
var y = "10";
if (x == y)    // returns true
cs




Strict Comparison (===)

1
2
3
var x = 10;
var y = "10";
if (x === y)    // returns false
cs






swtich 문에는 Strict Comparison 이 적용된다.

1
2
3
4
var x = 10;
switch(x) {
    case 10alert("Hello");    // display an alert
}
cs




1
2
3
4
var x = "10";
switch(x) {
    case 10alert("Hello");    // not display an alert
}
cs






출처 : http://www.w3schools.com/js/js_mistakes.asp

만약 함수가 호출될 때 인자를 다 넣어주지 않는다면, 넣어주지 않은 인자값들은 undefined가 된다.


그리고 이 undefined 값들은 함수 코드의 안정성을 해칠 수 있다.


따라서 인자값들에 대해서 default 값을 세팅해주는 습관을 들이는 것이 좋다.




1
2
3
4
5
function myFunction(x, y) {
    if (y === undefined) {
        y = 0;
    }
}
cs






출처 : http://www.w3schools.com/js/js_best_practices.asp

== 는 비교 전에 항상 matching type 으로 형변환을 시킨다.


=== 는 값 뿐만아니라 타입까지 강제적으로 비교한다.




1
2
3
4
5
6
7
0 == "";        // true
1 == "1";       // true
1 == true;      // true
 
0 === "";       // false
1 === "1";      // false
1 === true;     // false
cs






출처 : http://www.w3schools.com/js/js_best_practices.asp