반복자(Iterator)

Featured image

반복자(Iterator)


iterator는 데이터 컬렉션에 엑세스하고 탐색하기 위함 구조화된 메커니즘을 제공함. 이는 next()메서드를 소유한 객체로서 이를 통해 시퀀스의 다음 값을 검색하는 동시에 반복이 끝에 도달했는지 여부도 나타낼 수 있음.

고차 함수는 다른 함수에 작동하는 함수인데, 함수를 인수를 받아들이거나 함수를 결과로 반환할 수 있음. 이런 높은 수준의 추상화를 통해 함수 자체를 조작하고 유연화하여 재사용성 높은 코드를 만들 수 있음.

iterator와 고차 함수를 결함하면 iterator를 가이드로 사용해 데이터 컬렉션의 각 값에 특정 프로세스는 변환을 적용할 수 있음. 예를 들어 ‘map`함수를 활용해 주어진 함수를 배열의 각 요소에 적용하여 변환된 값으로 새 배열을 효과적으로 생성할 수 있으며 ‘reduce’ 함수를 사용하면 결합 함수를 적용해 배열의 값을 단일 결과로 집계할 수 있음.

iterator란?

객체 리터럴은 객체 생성에 대한 간단하고 편리한 접근 방식을 따름, 이는 중괄호로 묶인 속성 및 해당 값의 컬렉션으로 구성되며 이런 속성은 쉼표로 구분되고 키-값 쌍 형식임.

객체 리터럴을 활용해 생성자 함수 없이 객체를 신속하게 정의하고 초기화 할 수있음.

const person = { 
    firstName: "John",
    lastName: "Doe",
    age: 30
};

위 예시에는 person 객체에 firstName, lastName, age라는 세 가지 속성이 있음. 속성 이름과 값은 콜론으로 구분, 각 속성-값 쌍은 쉼표로 구분됨.

속성 할당(Property Assignment)

JS에서 객체에 속성 할당법은 다양하며 각 방법은 고유한 장점과 예가 있음.

  1. 점 표기법: 속성 이름에 대한 간단한 문자열을 사용. 객체에 속성을 할당함. 예: object.properyName = "value"; 속성이 이미 존재할 경우 해당 값이 업데이트 되며, 존재 하지 않는 경우 새 속성이 할당됨.

  2. 대괄호 표기법: 문자열 표현식을 속성 이름으로 사용해 객체에 속성을 할당 가능. 예: object["propertyName"] = "value"; 속성 이름에 특수 문자, 공백이 포함되거나 이름이 런타임으로 동적 생성되는 경우에 유용함.

  3. 객체 구조분해: 객체에서 속성을 추출하고 이를 동일한 이름의 변수에 할당할 수 있음. 예: const{propertyName} = object; 객체의 해당 속성 값을 보유하는 propertyName이라는 변수를 생성함.

  4. 스프레드 연산자: 한 객체에서 다른 객체로 속성을 복사하는데 사용함. 예: const newObject = {...object}; 원래 객체의 모든 속성을 포함하는 새 객체 newObject가 생성됨.

JS의 객체들은 변경 가능하며, 해당 속성은 런타임 중 동적으로 수정, 추가, 제거될 수 있음. 동일한 이름의 속성이 중첩 스코프 내에서 선언될 때 발생하는 속성 새도잉에 주의할 것. 이런 경우 내부 속성이 외부 속성보다 우선함.

속성 할당 규칙

  1. 단순 이름에 점 표기법 사용: 이름에 공백이나 특수문자가 없는 경우에 적합함.
  2. 특수 문자, 공백 이름에 대괄호 표기법 사용: 점 표기법을 사용할 수 없는 공백이나 특수문자가 포함된 이름에는 대괄호 표기법 사용.
  3. 객체 변경 가능성 이해
  4. Object.defineProperty() 고려: Object.defineProperty() 메서드를 사용해 객체의 속성을 정의하고 해당 값, 쓰기 가능성, 열거 가능성, 구성 가능성을 세밀하게 제어할 수 있음.
  5. 예약어를 속성이름으로 사용하지 말것 : 특정 목적의 특정 단어(null, undefine, NaN, Infinity, eval)을 이름으로 사용하면 안됨.
  6. 속성 새도잉에 주의할 것: 중첩된 스코프 내 새도잉 발생에 주의.
  7. 객체 병합을 위한 Object.assign()을 고려: Object.assign() 메서드는 두 개 이상의 객체를 단일 객체로 병합함. 여러 속성의 객체를 통합하는데 유용함.

고차 함수

다른 함수를 인수로 받거나 함수를 결과로 반환할 수 있는 함수임.

고차함수가 사용되는 2가지 일반적인 시나리오

  1. 인수로서의 함수: 하나 이상의 함수를 인수로 취하여 전달된 함수를 고차 함수의 본문 내 호출하거나 조작할 수 있음. 함수를 인수로 취하는 고차 함수의 예: map(), filter(), reduce().

  2. 반환값으로서의 함수: 본문 내에서 수행된 일부 조건이나 계산을 기반으로 새 함수를 생성하고 반환할 수 있음. 함수를 반환하는 고차 함수는 팩토리 함수, currying 및 함수 조합과 같은 시나리오에서 사용됨.

function callTwice(func){
    func();
    func();
}
function sayHello(){
    console.log("Hello");
}
callTwice(sayHello); // Hello, Hello

위의 예에서는 callTwicefunc 함수를 인수로 사용해 이를 두번 호출하기 때문에 고차 함수임. sayHello 함수는 callTwice에 인수로 전달됨.

고차 함수는 추상적이고 재사용 가능한 코드를 생성함으로서 상당한 이점을 제공함.

  1. .map 함수 이 함수는 double 함수를 인수로 받아 numbers 배열 내 각 요소에 적용함. 결과적으로 2 배의 값을 가진 새 배열 dooubleNumbers가 생성됨.
const numbers = [1, 2, 3, 4, 5];
const double = x => x * 2;
const doubledNumbers = numbers.map(double);
console.log(doubledNumbers); // [2, 4, 6, 8, 10];
  1. .filter 함수
const numbers = [1, 2, 3, 4, 5];
const isEven = x => x % 2 === 0;
const evenNumbers = numbers.filter(isEven);
console.log(evenNumbers); // [2, 4]

isEven이라는 함수를 매개변수로 받고 이를 활용하여 numbers라는 배열 내의 요소를 필터링함. 결과적으로 원 배열의 짝수로만 구성된 evenNumbers 라는 새로운 배열이 생성됨.

  1. reduce 함수
const numbers = [1, 2, 3, 4, 5];
const sum = (acc, x) => acc + x;
const total = numbers.reduce(sum, 0);
console.log(total); // 15

sum 이라는 함수를 매개변수로 받고, 이를 사용해 number 배열 내의 요소를 단일 값으로 줄임. sum 함수에는 acc라는 누산기와 x로 지정된 현재 값이라는 두 가지 인수가 필요함. reduce 함수는 초기 누산기 값 0 으로 프로세스를 시작하고 배열의 각 요소에 대해 sum 함수를 반복적으로 호출함. 이로 인해 모든 요소가 처리될 때까지 누산기 값이 업데이트 되어 최종 결과를 생성함.

iterator

iterator는 배열, 문자열, map, set 등과 같은 데이터 컬렉션의 정돈되고 제어된 순회를 용이하게 하는 매커니즘임. iterator는 이런 데이터 구조를 미리 정의된 예측 가능한 순서로 순회할 수 있도록 함. iterator로서 자격을 얻으려면 객체가 next() 메서드를 구현해야 함. 반복자 객체가 next()메서드를 호출하면 valuedone 두 속성을 가진 객체가 반환됨. value속성은 순회 과정에서 현재 값이고, done 속성은 순회가 완료되었는지 여부를 나타내는 부울 값임.

next()메서드가 호출되면 컬렉션에서 다음 값을 검색하고 이에 따라 iterator의 내부 상태를 업데이트함. 반복할 값이 더 이상 없으면 done속성이 true 가 되어 프로세스가 종료됨.

iterator는 구조화된 방식으로 데이터 컬렉션에 엑세스하고 탐색할 수 있게 해주는 next() 메서드가 있는 객체로, 현재 값을 제공하고 반복이 완료되었는지 여부를 나타냄.

iterator가 배치되는 방식의 기본 예시:

let array = [1, 2, 3];
let iterator = array[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false}
console.log(iterator.next()); // { value: 2, done: false}
console.log(iterator.next()); // { value: 3, done: false}
console.log(iterator.next()); // { value: undefined, done: true}

iterator를 사용하려면 객체에서 next() 메서드를 호출해야함. iterator.next()를 반복 호출하면 done속성이 반복의 끝을 나타내는 true가 될 때까지 컬렉션의 각 값에 엑세스 가능함. iterator는 JS의 기본이며 정의된 iterator 프로토콜을 준수하여 다양한 데이터 구조로 작업할 수 있는 일관되고 재사용 가능한 방법을 제공함.

iterator를 컬렉션의 진행 상황을 추적하여 한 번에 하나씩 각 요소에 엑세스 할 수 있게 해주는 마커로 생각할 수 있음.

const numbers = [1, 2, 3, 4];
const iterator = numbers[Symbol.iterator]();

let current = iterator.next();
while(!current.done){
    console.log(current.value);
    current = iterator.next();
}

iterator를 사용한 고차 함수

iterator와 함께 고차 함수를 사용하는 경우 next()메서드를 사용해 컬렉션의 각 요소에 고차 함수를 적용할 수 있음. 고차 함수는 iterator가 제공하는 현재 값을 가져왓 이에 대한 일부 논리 or 변환 작업을 수행하고 새 값을 반환함. iterator가 반복이 완료되었음을 나타낼 때까지 컬렉션의 각 요소에 대해 이 프로세스가 반복됨.

map 함수는 숫자 배열의 각 요소를 반복하기 위해 iterator와 함께 사용되며, 제공된 함수 (value) => value * 2 는 각 값에 2를 곱해 2배의 값으로 새 배열 doubledNumbers를 만듬.

마찬가지로 filter, reduce, forEach와 같은 다른 고차 함수들 역시 iterator와 함께 사용해 컬렉션에 대해 각각 반복작업을 수행할 수 있음.

let numbers = [1, 2, 3, 4, 5];
let squares = numbers.map(x => x * x);
console.log(sqaures); // [1, 4, 9, 16, 25]
let numbers = [1, 2, 3, 4, 5];
let evenNumbers = numbers.filter(x => x % 2 === 0);
console.log(evenNumbers); // [2, 4]
let numbers = [1, 2, 3, 4, 5];
let sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15
function delayedLog(text){
    setTimeout(() => {console.log(text);
    }, 1000);
}

delayedLog("Hello, World!");
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Hello, World!");
    }, 1000);
});
promise.then(value => { 
    console.log(value);
});

iterator와 고차 함수 사용 규칙

  1. iterator가 있는 고차 함수는 함수를 인수로 받거나 결과로 함수를 반환해야 함.
  2. iterator가 있는 고차 함수는 데이터 컬렉션에 대해 작동하려면 iterator와 함께 사용해야함.
  3. iterator 객체는 valuedone이라는 2가지 속성이 있는 객체를 반환하는 next() 메서드를 구현해야함.
  4. iterator가 있는 고차 함수는 특정 순서로 사용해야 함. 먼저 반복자가 고차 함수에 전달된 다음 반복자가 통과할 데이터 컬렉션이 전달됨. 이렇게 하면 고차 함수가 원하는 데이터에 대한 작동 보증이 됨.
  5. 함수형 프로그래밍 스타일에서는 반복자와 함께 고차 함수를 사용하는 것이 좋음, 여기서 명령문 대신 선언 방식으로 데이터를 변환 처리하는 작업이 포함됨.
  6. iterator와 함께 고차 함수를 사용할 때는 주의해야함.추상화와 명확성 사이 균형을 맞추는 것이 중요함.
  7. iterator와 함께 고차 함수를 활용하는 경우 쉽게 테스트하고 디버깅이 가능한 방식으로 설계하는 것이 좋음. 모듈식, 잘 구조화된 코드 작성, 우려 사항 분리, 모범 사례 준수 등이 포함.