이터러블
은 요소를 하나씩 차례로 반환 가능한 object
ES6에서 자바스크립트에 두 가지 새로운 개념인 iterable
과 iterator
를 도입했다.
Iterable
하다는건, 순회가 가능하다는 것이다 또한 순회가 가능하면 관련된 반복문, 연산자들을 사용할 수 있다.
Iterable
이란 일반적으로 데이터를 소비할 수 있는 데이터 구조이다. 주로 반복자를 반환하는 Symbol.iterator
가 key
인 method
를 구현하여 수행한다.
Javascript
의 데이터 소스가 있는 경우 데이터를 구조화하는 방법에 따라 다른 방식으로 소비해야 한다.
Array
의 데이터를 반복하고 소비하는 것은 { key, value }
객체와 다르다.
새로운 데이터 소스를 각각 다른 방법으로 소비하는 것은 실용적이지 않다.
즉 모든 데이터 소스가 동일한 방식으로 소비될 수 있는 인터페이스가 필요하다.
Iterable
은 Symbol.iterator
메서드가 구현되어 있어야 한다.
구현한 [Symbol.iterator]
메서드의 결과는 iterator
라고 부른다. 이 iterator
는 이어지는 반복 과정을 처리한다.
iterator
객체에는 { done: Boolean, value: any }
를 반환하는 메서드 next()
가 반드시 구현되어야 한다.
iterator
객체의 done
속성은 반복의 종료 유무를 의미하고, value
속성은 다음 값을 나타낸다.
values()
entries()
keys()
const arr = [1, 2, 3];
console.log(arr.values()); // Object [Array Iterator] {}
console.log(arr.entries()); // Object [Array Iterator] {}
console.log(arr.keys()); // Object [Array Iterator] {}
IterableIterator
객체를 활용하여 메서드 사용const array = [1, 2, 3];
for (let item of array.values()) {
console.log(item); // 1 2 3
}
// array자체가 iterable하기 때문에 메서드 생략가능
for (let item of array) {
console.log(item); // 1 2 3
}
const array = [1, 2, 3];
const iterator = array.values();
while(true) {
const item = iterator.next();
if (item.done) break;
console.log(item.value); // 1 2 3
}
iterator
함수가 없기 때문에 of
연산자를 사용할 수 없다.const obj = { id: 123, name: 'mirrer' };
for (const item of obj) {
console.log(item); // TypeError: obj is not iterable
}
key
를 가져오는 in
연산자는 사용 가능하다.const obj = { id: 123, name: 'mirrer' };
for (const key in obj) {
console.log(key); // id name
}
Iterator
를 간편하게 만드는 방법
일반 함수는 하나의 값만을 반환합니다.
하지만 generator
를 사용하면 여러 개의 값을 필요에 따라 하나씩 yield
(반환)할 수 있다.
제너레이터와 이터러블 객체를 함께 사용하면 손쉽게 데이터 스트림
을 만들 수 있다.
제너레이터를 만들기 위해서는 제너레이터 함수
라 불리는 특별한 문법 구조, function*
이 필요하다.
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
제너레이터 함수는 일반 함수와 동작 방식이 다르다.
제너레이터 함수는 호출하면 코드가 실행되지 않고, 대신 실행을 처리하는 특별 객체, '제너레이터 객체’가 반환된다.
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
// '제너레이터 함수'는 '제너레이터 객체'를 생성
let generator = generateSequence();
console.log(generator); // [object Generator]
제너레이터의 주요 메서드 next()
는 호출하면 가장 가까운 yield <value>
문을 만날 때까지 실행이 지속된다.
yield <value>
문을 만나면 실행이 멈추고 산출하고자 하는 값인 value
가 바깥 코드에 반환된다.
next()
는 항상 value
, done
두 프로퍼티를 가진 객체를 반환한다.
function* multipleGenerator() {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i ** 2; // 다음 사용자가 next를 호출할때까지 대기
}
}
const multiple = multipleGenerator();
let next = multiple.next(); // 0
next = multiple.next(); // 1
next = multiple.next(); // 2
Generator return
: 제너레이터를 종료// generator사용
function* multipleGenerator() {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i ** 2; // 다음 사용자가 next를 호출할때까지 대기
}
}
const multiple = multipleGenerator();
let next = multiple.next(); // 0
multiple.return(); // 제너레이터의 반복을 종료
next = multiple.next(); // 출력 X
next = multiple.next(); // 출력 X
Generator throw
: 제너레이터의 에러실행// generator사용
function* multipleGenerator() {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i ** 2; // 다음 사용자가 next를 호출할때까지 대기
}
}
const multiple = multipleGenerator();
let next = multiple.next(); // 0
multiple.throw('Error!!'); // 제너레이터의 에러실행
next = multiple.next(); // 출력 X
next = multiple.next(); // 출력 X
발생한 에러는 try ~ catch
문으로 해결
// generator사용
function* multipleGenerator() {
try {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i ** 2; // 다음 사용자가 next를 호출할때까지 대기
}
} catch (error) {
console.log(error);
}
}
const multiple = multipleGenerator();
let next = multiple.next(); // 0
multiple.throw('Error!!'); // 제너레이터의 에러실행
next = multiple.next(); // 출력 X
next = multiple.next(); // 출력 X
배열, 문자열, 객체 등 반복 가능한 객체 (Iterable Object)를 개별 요소로 분리
ECMAcmaScript 2018에 추가된 스프레드 연산자는 순회가 가능한 모든 것들은 펼칠 수 있다.
즉 모든 Iterable
은 Spread
가될 수 있다. (func(...iterable), [...iterable], { ...obj }
)
function add(a, b, c) {
return a + b + c;
}
const nums = [1, 2, 3];
// console.log(add(nums[0], nums[1], nums[2])); -> X
console.log(add(...nums)); // 6
function sum(first, second, ...nums) {
console.log(nums);
}
sum(1, 2, 3, 4, 5, 6); // 3 4 5 6
const fruits1 = ['apple', 'peach'];
const fruits2 = ['banana', 'watermelon'];
let arr = fruits1.concat(fruits2);
console.log(arr); // [ 'apple', 'peach', 'banana', 'strawberry' ]
arr = [...fruits1, ...fruits2];
console.log(arr); // [ 'apple', 'peach', 'banana', 'strawberry' ]
arr = [...fruits1, 'grape' , ...fruits2];
console.log(arr); // [ 'apple', 'peach', 'grape', 'banana', 'strawberry' ]
const mirrer = { name: 'mirrer', age: 30 };
const updated = {
...mirrer,
job: 'web developer',
}
// 새로운 오브젝트를 생성해서 기존의 객체에는 변화 X
console.log(mirrer); // { name: 'mirrer', age: 30 }
console.log(updated); // { name: 'mirrer', age: 30, job: 'web developer' }
데이터를 그룹화하여 쉽게 만들 수 있는 표현식
구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript
표현식이다.
구조 분해 할당을 사용하면 배열안에서도 의미 있는 변수명을 전달할 수 있다.
const fruits = ['apple', 'banana', 'watermelon', 'peach', 'orange'];
const [first, second, ...others] = fruits;
console.log(first); // apple
console.log(second); // banana
console.log(others); // [ 'watermelon', 'peach', 'orange' ]
const point = [1, 2];
const [x, y, z = 9] = point;
console.log(x); // 1
console.log(y); // 2
console.log(z); // 9
function createFruits() {
return ['apple', 'banana'];
}
const [fruit1, fruit2] = createFruits();
console.log(fruit1); // apple
console.log(fruit2); // banana
const mirrer = { name: 'mirrer', age: 30, job: 'web developer' };
function display({ name, age, job }) {
console.log(name);
console.log(age);
console.log(job);
}
display(mirrer); // mirrer 30 web developer
const mirrer = { name: 'mirrer', age: 30, job: 'web developer' };
const { name, age, job: occupation, pet = '강아지' } = mirrer;
console.log(name); // mirrer
console.log(age); // 30
console.log(occupation); // web developer
console.log(pet); // 강아지
Iteration protocols - JavaScript | MDN
Generator - JavaScript - MDN Web Docs
Spread operator - JavaScript | MDN - LIA
구조 분해 할당 - JavaScript | MDN
모던 자바스크립트 Deep Dive
모던 JavaScript 튜토리얼