본문 바로가기

2022-2 웹개발 스터디

[모던 JS] CH 34. 이터러블, CH 35. 스프레드 문법, CH 36. 디스트럭처링 할당 # 20

CH 34. 이터러블

34.1 이터레이션 프로토콜

34.1.1 이터러블

- 이터러블 프로토콜을 준수한 객체

- Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체

- 이터러블은 for ... of 문으로 순회할 수 있으며 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용

const arr = [1, 2, 3];

console.log(Symbol.iterator in arr); // true

// for ... of 문으로 순회 가능
for (const item of arr) {
    console.log(item);
}

// 스프레드 사용
console.log([...arr]); // [1, 2, 3]

34.1.2 이터레이터

- 이터레이터 프로토콜을 준수한 객체

- 이터러블의 요소를 탐색하기 위한 포인터 역할

- 이터러블의 Symbol.iterator 메서드가 반환한 이터레이터는 next 메서드를 가짐

const arr = [1, 2, 3];

const iterator = arr[Symbol.iterator]();
console.log('next' in iterator); // true

34.2 빌트인 이터러블

- 표준 빌트인 이터러블

34.3 for ... of 문

- 프로퍼티 어트리뷰트의 값이 참인 프로퍼티를 순회하며 열거함

- 프로퍼티 키가 심벌인 프로퍼티는 열거하지 않음

for (const item of [1, 2, 3]) {
    console.log(item); // 1 2 3 
}

34.4 이터러블과 유사 배열 객체

- 유사 배열 객체는 이터러블이 아닌 일반 객체

: Symbol.iterator 메서드가 없어 for ... of 문으로 순회할 수 없음

34.5 이터레이션 프로토콜의 필요성

- 이터레이션 프로토콜은 다양한 데이터 공급자가 하나의 순회 방식을 갖도록 규정하여

  데이터 소비자가 효율적으로 다양한 데이터 공급자를 사용할 수 있도록

  데이터 소비자와 데이터 공급자를 연결하는 인터페이스의 역할을 함

34.6 사용자 정의 이터러블

34.6.1 사용자 정의 이터러블 구현

- 이터레이션 프로토콜을 준수하지 않는 일반 객체도 이터레이션 프로토콜을 준수하도록 구현하면 사용자 정의 이터러블이 됨

// 피보나치 수열을 구현한 사용자 정의 이터러블
const fibo = {
    // Symbol.iterator 메서드를 구현하여 이터러블 프로토콜을 준수
    [Symbol.iterator]() {
        let [pre, cur] = [0, 1];
        const max = 10;
        
        return {
            next() {
                [pre, cur] = [cur, pre + cur];
                return {value:cur, done:cur >= max};
            }
        };
    }
};

for (const num of fibo) {
    console.log(num); // 1 2 3 5 8
}

34.6.2 이터러블을 생성하는 함수

- 피보나치 수열의 예에서 수열의 최대값을 외부에서 전달할 수 있도록 수정

- 수열의 최대값을 인수로 전달받아 이터러블을 반환하는 함수를 만들면 됨

// 피보나치 수열을 구현한 사용자 정의 이터러블
// 수열의 최대값을 인수로 전달받는다
const fibo = function (max) {
    let [pre, cur] = [0, 1]; 
        
    return {
        [Symbol.iterator]() {
            return {
                next() {
                    [pre, cur] = [cur, pre + cur];
                    return {value:cur, done:cur >= max};
                }
            };
        }
    };
};

for (const num of fibo(10)) {
    console.log(num); // 1 2 3 5 8
}

34.6.3 이터러블이면서 이터레이터인 객체를 생성하는 함수

// 이터러블이면서 이터레이터인 객체를 반환하는 함수
const fibo = function (max) {
    let [pre, cur] = [0, 1]; 
        
    return {
        [Symbol.iterator]() {return this;},
         next() {
             [pre, cur] = [cur, pre + cur];
              return {value:cur, done:cur >= max};
         }
    };
};

// iter는 이터러블이면서 이터레이터
let iter = fibo(10);

// for ... of 문으로 순회할 수 있음
for (const num of fibo(10)) {
    console.log(num); // 1 2 3 5 8
}

34.6.4 무한 이터러블과 지연 평가

// 무한 이터러블을 생성하는 함수
const fibo = function () {
    let [pre, cur] = [0, 1]; 
        
    return {
        [Symbol.iterator]() {return this;},
         next() {
             [pre, cur] = [cur, pre + cur];
              return {value:cur};
         }
    };
};

// for ... of 문으로 순회할 수 있음
for (const num of fibo()) {
    if (num > 10000) break;
    console.log(num); // 1 2 3 5 8 ... 4181 6765
}

CH 35. 스프레드 문법

- 스프레드 문법의 결과물은 값으로 사용할 수 없고, 쉼표로 구분한 값의 목록을 사용하는 문맥에서만 사용할 수 있음

1. 함수 호출문의 인수 목록

2. 배열 리터럴의 요소 목록

3. 객체 리터럴의 프로퍼티 목록

35.1 함수 호출문의 인수 목록에서 사용하는 경우

- 문제가 생기는 경우?

: Math.max 메서드에 숫자가 아닌 배열을 인수로 전달하면 최대값을 구할 수 없으므로 NaN 반환

var arr = [1, 2, 3];

// 스프레드 문법을 사용하여 1, 2, 3으로 펼쳐 전달
const max = Math.max(...arr); // 3

35.2 배열 리터럴 내부에서 사용하는 경우

35.2.1 concat

- 2개의 배열을 1개의 배열로 결합하고 싶은 경우

// ES5
var arr = [1, 2].concat([3, 4]);
console.log(arr); // [1, 2, 3, 4]

// ES6
const arr = [...[1, 2], ...[3, 4]];
console.log(arr); // [1, 2, 3, 4]

35.2.2 splice

const arr1 = [1, 4];
const arr2 = {2, 3};

arr1.splice(1, 0, ...arr2);
console.log(arr1); // [1, 2, 3, 4]

35.2.3 배열 복사

- 원본 배열의 각 요소를 얕은 복사하여 새로운 복사본 생성

// ES5
var origin = [1, 2];
var copy = origin.slice();

console.log(copy); // [1, 2]
console.log(copy === origin); // false

// ES6
const origin = [1, 2];
const copy = [...origin];

console.log(copy); // [1, 2]
console.log(copy === origin); // false

35.2.4 이터러블을 배열로 반환

// 방법1
function sum() {
    return [...arguments].reduce((pre, cur) => pre + cur, 0);
}

console.log(sum(1, 2, 3)); // 6

// 방법2
// Rest파라미터를 사용하는 것
const sum = (...args) => args.reduce((pre, cur) => pre + cur, 0);
console.log(sum(1, 2, 3)); // 6

35.3 객체 리터럴 내부에서 사용하는 경우

// 객체 병합
const merged = { ...{x:1, y:2}, ...{y:10, z:3}};
console.log(merged); // {x:1, y:10, z:3}

// 특정 프로퍼티 변경
const changed = { ...{x:1, y:2}, y:100};
console.log(changed); // {x:1, y:100}

// 프로퍼티 추가
const added = { ...{x:1, y:2}, z:0};
console.log(added); // {x:1, y:2, z:0}

CH 36. 디스트럭처링 할당

36.1 배열 디스트럭처링 할당

- ES6의 배열 디스트럭처링 할당은 배열의 각 요소를 배열로부터 추출해 1개 이상의 변수에 할당함

- 배열 디스트럭처링 할당의 대상은 이터러블이어야 하며, 할당 기준은 배열의 인덱스 (순서)

- 배열 디스트럭처링 할당을 위해서는 할당 연산자 왼쪽에 값을 할당받을 변수를 선언해야 함

- 우변에 이터러블을 할당하지 않으면 에러가 발생 

- 배열 디스트럭처링 할당을 위한 변수에 Rest 파라미터와 유사하게 Rest 요소를 사용할 수 있음

- 이때 Rest 요소는 반드시 마지막에 위치해야 함

const arr = [1, 2, 3];
const [one, two, three] = arr;

console.log(one, two, three); //  1 2 3

const [x, ...y] = [1, 2, 3];
console.log(x, y); // 1 [2, 3]

36.2 객체 디스트럭처링 할당

- ES6의 객체 디스트럭처링 할당은 객체의 각 프로퍼티를 객체로부터 추출해 1개 이상의 변수에 할당함

- 할당 기준은 프로퍼티 키 (순서는 의미가 없으며, 프로퍼티 키가 일치하면 할당)

- Rest 프로퍼티 사용 가능 (반드시 마지막에 위치해야 함)

const user = {firstName:'HB', lastName:'Kim'};

const {lastName, firstName} = user;

console.log(firstName, lastName); // HB kim


// Rest 프로퍼티
const {x, ...rest} = {x:1, y:2, z:3};
console.log(x, rest); // 1 {y:2, z:3}