-> 코드가 계산(Evaluation) 되어 값을 만드는 것
-> 값
으로 다룰 수 있다.
-> 변수
에 담을 수 있다.
-> 함수의 인자
로 사용될 수 있다.
-> 함수의 결과
로 사용될 수 있다.
const a = 10;
const add10 = a => a + 10;
const r = add10(a); // 20
-> 함수를 값
으로 다룰 수 있다.
-> 조합성과 추상화의 도구이다.
const add5 = a => a + 5;
console.log(add5); // a => a + 5 (f)
console.log(add5(5)); // 10;
const f1 = () => () => 1;
console.log(f1()); // () => 1 (f)
const f2 = f1();
console.log(f2); // () => 1 (f)
console.log(f2()); // 1
-> 함수를 값으로 다루는 함수
1) 함수를 인자로 받아서 실행하는 함수 (callback)
2) 함수를 만들어 리턴하는 함수 (curry)
-> 클로저를 만들어 리턴하는 함수
// callback exam
const apply1 = f => f(1);
const add2 = a => a + 2;
console.log(apply1(add2)); // 3
const times = (f, n) => {
for(let i=0; i<n; i++) {
f(i);
}
};
times(console.log, 3); // 0, 1, 2
times(a => console.log(a + 10), 3); // 10, 11, 12
// curry exam
const addMaker = a => b => a + b;
const add10 = addMaker(10);
console.log(add10);// b => 10 + b; (f)
console.log(add10(5); // 15
const list = [1, 2, 3];
// es5
for(var i=0; i<list.length; i++_ {
console.log(list[i]);
}
// es6
for(let el of list) {
console.log(el);
}
const arr = [1, 2, 3];
for(const a of arr) console.log(a);
const set = new Set([1, 2, 3]);
for(const a of set) console.log(a);
const map = new Map(['a',1], ['b',2], ['c',3]]);
for(const a of map) console.log(a);
for...of
는 어떤원리로 돌아갈까?
키워드는 Symbol.iterator
const arr = [1, 2, 3];
arr[Symbol.iterator] = null;
for(const a of arr) console.log(a);
// Uncaught TypeError
// arr is not iterable
위에서 Symbol.iterator
를 null을 줌으로써 반복문에서 에러가 발생한다. 이는 Set
, Map
에도 동일하다.
이걸 통해서 for...of
반복은 위의 속성과 관계가 있다는것을 알 수 있다.
[Symbol.iterator]()
를 가진 값{value, done }
객체를 리턴하는 next()
를 가진 값for...of
, 전개 연산자 등과 함께 동작하도록한 규약arr[Symbol.iterator]; // values() { [native code] }
let iterator = arr[Symbol.iterator]();
iterator.next(); // {value: 1, done: false}
iterator.next(); // {value: 2, done: false}
iterator.next(); // {value: 2, done: false}
iterator.next(); // {value: undefined, done: true}
위에서 심볼 이터레이터를 실행시켜 리턴받은 이터레이터를 통하여
next()
를 호출했을 때 value의 값과 done의 값을 가지는 객체가 리턴된다.
그리고 이 value의 값들은 배열의 요소들이고 모든 요소들을 다 가져왔을 때 done은 true가 된다.
for...of
나 전개 구문에서 원리가 이런것이다.
1. arr[Symbor.iterator]()
를 실행하여 이터레이터 객체 리턴
2. next()
메서드를 통하여 요소값으로 받아온 value를 넘겨줌
3. done
이 true이면 반복문을 빠져나옴
실제로 아래와 같은 코드를 작성하면 반복문이 생각한것보다 덜 돈다.
let arr = [1, 2, 3];
let iterator = arr[Symbol.itorator]();
iterator.next();
iterator.next();
for(const a of iterator) {
console.log(a); // 3이라는 요소 한번만 찍힌다.
}
for...of
문에서 이터레이터 next()
를 호출하는것을 이미 2번이나 미리 실행시켰기 때문에 실제적으로 반복문에서는 3이라는 숫자 한번만 찍히게 된다.
const iterable = {
[Symbol.iterator]() {
let i = 3;
return {
next() {
return i == 0 ? {done: true} : { value: i-- , done: false }
},
[Symbol.iterator]() { return this; }
}
}
};
let iter = iterable[Symbol.iterator]();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
이처럼 사용자 정의 이터러블 객체를 만들때에 중요하게 생각해야될 것은 [Symbol.iterator]
를 만들어 줘야한다는 것이다.
또한 리턴되는 이터러블에도 이 심볼이터레이터 메서드가 있어야한다.
웹브라우저 환경, 오픈라이브러리 등등 많은 곳에서 이 프로토콜을 이용하고 있다.
전개 연산자
const a = [1, 2]; a[Symbol.iterator] = null; console.log(...a); // error
전개 연산자 또한 이터레이터객체를 사용하는 것이다.
... 솔직히 말해서 당장 100%이해가 되지는 않는다. 하지만 좀더 다양하게 경험을하고 다시한번 이 개념을 봤을 때 더 많은것이 보이게끔 성장하고 다시 공부해볼것이다.