dokite 2022. 8. 3. 23:27

서문)

iterable(반복 가능한) 객체는 배열을 일반화한 객체이다

iterable 개념을 사용하면 어떤 객체든 for..of 반복문을 사용할 수 있다

iterable 개념 중에 대표적인 것이 배열인데 이외에도 다수의 내장 객체가 반복이 가능하다 (문자열…)

배열이 아닌 객체는, 어떤 것들의 목록이나 집합 등을 나타내고 있는 경우 for..of 문법을 적용해 순회할 수 있다

 

Symbol.iterator

 

// 객체에 for..of 적용하기

let range = {

from: 1,

to: 5

};

 

range[Symbol.iterator] = function() { // Symbol.iterator가 없으면 error

return {

current: this.from,

last: this.to,

 

next() {

if(this.current <= this.last) {

return{ done: false, value: this.current++ };

} else {

return { done : true };

}

}

};

};

 

for (let num of range) {

console.log(num); // 1, 2, 3, 4, 5

}

 

// range 자체에는 next() 메서드가 존재하지 않는다

// range[Symbol.iterator]를 호출해서 만든 iterator 객체와 이 객체의 메서드 next()에서 반복에 사용될 값을 만든다

 

iterable 객체의 핵심은 “관심사의 분리”

  • 반복 대상인 객체와 iterator 객체 분리(위 예시에선 range 객체와 range[Symbol.iterator])

 

// iterator 객체 + 반복 대상인 객체 = range(iterator로 만들어버리기)

let range = {

from: 1,

to: 5

 

[Symbol.iterator] () {

this.current =  this.from

return this;

},

 

next() {

if(this.current <= this.to) {

return { done: false, value: this.current++ };

} else {

return { done: true };

}

}

};

 

for (let num of range) {

console.log(num); // 1, 2, 3, 4, 5

}

 

Q.iterable 객체를 사용하면서 두개의 for..of를 사용하는 경우는 과연 어떤 경우일까?

 

무한개의 iterator

위 예시에서 range.to에 infinity를 할당하면 range가 무한대가 된다

무수히 많은 의사 난수를 생성하는 iterable 객체를 만들게 될 수 있다

 

또한 next()엔 제약이 없기 때문에 무한대가 된 range의 값을 계속 반환하는 것도 정상적인 동작이다

 

그렇지만 break 를 사용하면 언제든지 반복을 멈출 수 있다

 

문자열은 iterable 이다

앞서 말했듯이 배열과 문자열은 iterable 개념 중 대표적인 내장 iterable 이다

 

// for..of는 문자열의 각 글자를 순회한다

for (let char of “test”) {

console.log(t, e, s, t)

}

 

//서로케이트 쌍에도 잘 동작한다

let str = '𝒳😂';

for (let char of str) {

    console.log( char ); // 𝒳와 😂가 차례대로 출력됨

} 

 

iterator를 명시적으로 호출하기(거의 없긴 하지만 더 디테일 하게 반복 과정을 통제할 수 있다고 한다)

 

// 직접 호출을 통해서 순회해보자(수동으로 값을 가져옴)

let str = “Hello”;

 

// for (let char of str) console.log(char) 과 똑같이 동작한다

let iterator = str[Symbol.iterator] ();

 

while (true) {

let result = iterator.next();

if (result.done) break;

console.log(result.value); // H, e, l, l, o

}

 

iterable과 유사 배열

  • iterable: Symbol.iterator 메서드가 구현된 객체
  • array-like(유사 배열): index와 length 프로퍼티가 있어서 배열처럼 보이는 객체

 

iterable ≠ array-like

주로 iterable 이면서 유사 배열 객체인 문자열을 볼 수 있다

 

// 유사 배열에서 Symbol.iterator이 없어서 에러가 발생한 상황

let arraLike = {

0: “Hi”,

1: “world”,

length: 2

};

 

for (let item od arrayLike) { } // TypeError: undefined is not a function (near '...item of arrayLike...')

 

둘 다 객체이므로 배열 메서드(pop, push etc.)를 사용할 수 없다

방법이 있을까?

 

Array.from(범용 메서드)

: iterable 객체와 array-like 객체를 받아 “Real Array” 를 (새로운 배열안에 객체 요소들을 넣어) 만들어 준다 ➡️ 배열 메서드 사용 가능 !

 

// array-like ➡️ 새로운 배열

let arraLike = {

0: “Hi”,

1: “world”,

length: 2

};

 

let arr = Array.from(arraylike);

console.log(arr.pop()); // world

 

Q.length: 2가 아닌 이유는 단순히 길이를 나타내는 프로퍼티여서?

 

// iterable ➡️ 새로운 배열

let range = {

from: 1,

to: 5

};

 

let arr = Array.from(range);

console.log(arr); // 1, 2, 3, 4, 5

 

//Array.from에 mapping function을 옵션으로 넣어줄 수 있다

Array.from(obj [, mapFn, thisArg])

 

let arr = Array.from(range, num => num * num);

console.log(arr); 

 

 Q.mapping function는 매핑가능한 적용할 함수를 가르키는 건가?

 

  • 새로운 배열에 객체 요소를 넘겨주기 전 각 요소에 매핑함수를 적용해 반환할 수 있다
  • thisArg는 각 요소의 this를 지정할 수 있게 해준다

 

// 문자열 ➡️ 새로운 배열

let str = '𝒳😂';

 

// str를 분해해 글자가 담긴 배열로 만듦, 서로게이트 쌍에도 적용된다

let chars = Array.from(str);

 

alert(chars[0]); // 𝒳

alert(chars[1]); // 😂

alert(chars.length); // 2

 

// Array.from을 사용하여 서로게이트 쌍을 처리할 수 있는 slice 구현하기

 

let slice = (str, start, end) {

return Array.from(str).slice(start, end).join(“”);

};

 

let str = “𝒳😂𩷶”;

console.log(slice(str, 1, 3); // 😂𩷶

 

// Array.from 사용하지 않고 내장 str.slice 사용하면 서로게이트 쌍을 처리하지 않는다