파크로그
article thumbnail

무엇이 배열로 형변환이 자유로운거지?

평소 자바스크립트에서 "문자열" 을 다룰 때, Array.from() 메서드를 사용하거나 ... (스프레드 연산자) 를 사용해서 

문자열 <-> 배열의 형변환을 자유롭게 합니다.

 

1. "ABCDEF" 에서 "D" 를 없애주세요

const string = "ABCDEF";
const strArr = string.split("");
strArr.splice(strArr.indexOf("D"), 1);
const newStr = strArr.join("");
console.log(newStr); // "ABCEF"

 

2.  위에선 indexOf 를 사용했지만, 간단히 고차함수를 사용할 수도 있습니다.

const string = "ABCDEF";
const strArr = string.split("");
const newStr = strArr.filter(char => char !== "D").join("");
console.log(newStr); // "ABCEF"

 

3. 여기서, 문자열<->배열 을 형변환할 때 사용했던 split 을 간단히 ... 스프레드 연산자를 사용해서 바꿀 수도 있습니다.

const string = "ABCDEF";
const newStr = [...string].filter(char => char !== "D").join("");
console.log(newStr); // "ABCEF"

 

문자열 이외에도, 다양한 자료구조들을 배열로 형변환해서 사용할 수 있습니다.

문자열, HTML 의 태그를 querySelectorAll 로 받아온 NodeList, Set 과 같은 자료구조 들이 있습니다.

const strArr = [..."문자열"];
const elements = [...document.querySelectorAll("li")];
const item = [...new Set([1, 1, 2, 3])];

그런데 이렇게 스프레드 연산자(...) 를 사용해서 형변환이 가능한 이유가 무엇일까요?

저는 리스트 형태처럼 반복되는 자료구조는 다 될 것같이 느껴졌습니다.

 

그런데, 이 객체는 반복되는 자료구조라고 말할 수 있을까요?

 

const obj = { 
    0: "a", 
    1: "b", 
    2: "c",
    length:3
}

위 객체는 키값들이 인덱스 의 형태를 띄고, length 프로퍼티를 갖고있으므로 for 문을 통해서 반복이 가능합니다.

for(let i=0; i<obj.length; i++){
    console.log(obj[i]); // a b c
}

 

그렇다면 이 객체는 반복을 할 수있는 객체이니까, 스프레드 연산자를 사용하면 배열이 될 것 같다는 생각이 드는데요,

실제로 사용해보았습니다.

console.log([...obj]); // Uncaught TypeError: obj is not iterable

응? obj 는 iterable 이 아니라는 TypeError 가 나오는데요, 그렇다면 for 문을 돌 수있어도 iterable 은 아니라는 말인가? 라는 생각이드네요,

그렇다면 배열로 형변환을 할 수없는걸까요?  해당 객체는 Array.from() 메서드를 사용해 배열로 형변환할 수 있습니다.

 

Array.from(obj); // ['a', 'b', 'c']

 

어떤 건 for 문이 되는데 스프레드 문법으로 형변환이 안되고, Array.from() 메서드로는 가능하고 대체 뭐가 되고 뭐는 안되는걸까요?

 

mdn 문서를 보면 Array.from 은 아래와 같이 설명되어있습니다.

 

Array.from 메서드는 유사 배열 객체(array-like object)  반복 가능한 객체(iterable object) 를 얕게 복사해 새로운 Array 객체를 만듭니다.

 

유사 배열 객체는 뭐고, 반복 가능한 객체는 또 뭔가요?

유사 배열 객체(array-like object), 반복 가능한 객체(iterable object)

배열에 대해서 알아보고있는데 왠 객체이야기가 나오나? 라고 궁금할 수 있는데요,

자바스크립트에서는 배열 이라는 자료구조는 사실 배열이 아닙니다.

 

배열임을 흉내낸 특수 객체인데요, 자바스크립트에서 배열은 유사 배열 객체 이자 반복 가능한 객체 입니다.

 

여기서, 반복 가능한 객체(iterable object) 에 대해서 좀 더 자세히 알아보겠습니다.

 

반복 가능한 객체(iterable object)

반복 가능하다 라는 뜻에서, 이터러블(iterable) 이라는 말이 쓰였습니다.

반복 가능한 객체(iterable object) 는 이터러블 프로토콜(iterable protocol) 을 준수한 객체 라고 하는데요,

그럼 또 이터러블 프로토콜이 무엇이냐가 궁금해집니다.

이터러블 프로토콜(Iterable Protocol)

이터러블 프로토콜은

1. Symbol.iterator 를 키로 갖는 메서드를 가져야 하고,

2. 그 메서드는 호출 시 특정 형태 의 객체를 반환해야합니다.

 

1과 2의 조건을 만족하면 이터러블 프로토콜을 따른 객체가 되며, 반복 가능한 객체가 됩니다.

 

메서드를 어떻게 가져야 하는지, 특정 형태는 어떤 형태인지 에 대한 코드와 설명은 참고를 위해 간략히 작성하니, 참고하시면 좋겠습니다. 

Well-known Symbol

  • 자바스크립트가 기본 제공하는 빌트인 심벌값
  • Symbol.iterator 를 키로 갖는 메서드 를 가진다.
  • Symbol.iterator 메서드를 호출 하면 이터레이터 를 반환
    • 이터레이터는 next 메서드를 소유
    • next 를 호출하면 순회하며 value 와 done 프로퍼티 를 갖는 이터레이터 리절트 객체를 반환
  • 일반 객체를 이터러블처럼 동작하도록 구현하고 싶다면 위 이터레이션 프로토콜을 따르면 됩니다.
    • Symbol.iterator 를 키로 갖는 메서드를 객체에 추가하고 이터레이터를 반환하도록 구현합니다.
const iterable = {
	[Symbol.iterator]() {
		let cur = 1;
		const max = 5;
		const iterator = {
			next() {
				return { value: cur++, done: cur > max + 1 };
			}
		};
		return iterator;
	}
};

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

 

이터러블은 그래서?

그렇다면 이터러블인 자료구조의 프로토타입 체인을 따라가면 Symbol.iterator 관련 메서드를 찾아볼 수 있을거라는 생각이 듭니다.

Symbol.iterator 를 프로퍼티 키로 갖고있는 자료구조( = 이터러블인 자료구조 ) 는 다양한 자료구조들이 있습니다.

- Array, String, Map, Set, NodeList, HTMLCollection, arguments, TypedArray...

 

위 자료구조들의 prototype chain 을 따라가보면, Symbol.iterator 를 확인할 수 있게됩니다.

 

객체가 이터러블 프로토콜을 따라서 이터러블 객체가 된다면 아래와 같은 것들이 가능해집니다.

1. for-of 를 통한 반복

2. 스프레드 연산자 사용 (...) 을 통한 배열 형변환

3. 배열 디스트럭처링 할당

// 배열 디스트럭처링 할당
const [a, b] = [1, 2, 3];
console.log(a); // 1

for-in, for-of, for 문

처음 스프레드 연산자가 되는지, 안되는지 에 대한 판단을 "for 문이니까 되는거 아닌가?" 라고 생각했는데요,

그렇다면 for 문, for-of, for-in 등의 반복문은 어떤 조건에서 가능한 걸까요?

 

이터러블은 기대와 달리 for-in, for 문을 통한 반복이 ❌ 불가능 합니다.

( 단순 이터러블 이기만 하면 반복되어 보여지는 것이 없다는 뜻 입니다.)

 

MDN 사이트에서 확인해보면 for-in, for-of 는 아래 처럼 동작한다고 합니다.

for...of

  • 이터러블을 순회하면서 이터러블의 요소(value) 를 변수에 할당

for...in

  • 프로토타입 체인상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어트리뷰트 [[Enumerable]] 값이 true 인 프로퍼티를 순회하며 열거한다.

 

for-in 의 설명을 보면 객체의 프로퍼티 키 중에서 그 속성 [[Enumerable]] 의 값이 true 인 것을 열거한다고 되어있는데요,

그렇다면 객체의 키를 열거하는 반복문이라는 생각이 듭니다.

 

이제 여기서 유사 배열 객체 에 대해서 알아보겠습니다.

 

유사배열객체

  • 인덱스 로 프로퍼티값에 접근할 수 있고 length 프로퍼티 를 갖는 객체
  • length 프로퍼티를 갖고있기 때문에 for 문으로 순회할 수 있음
  • 인덱스 를 나타내는 숫자를 키로 가짐
const arrayLike = {
	0:1,
	1:3,
	2:5,
	length:3
}

for(let i=0; i<arrayLike.length; i++){
	console.log(arrayLike[i]); // 1 3 5
}
  • 유사배열객체는 이터러블 이 아닌 일반 객체 이므로 for...of 가 불가능합니다.
    • for...in 은 가능하지만 length 까지 보여진다.
    • 실제 배열 ( [1,2,3] ) 을 사용하면 안보여지는데?
      • 객체 리터럴을 통해 객체를 생성하면 모든 속성의 [[Enumerable]]true 이기 때문입니다. 실제 배열은 false 이겠죠?

 

  • Array, arguments, HTMLCollection 등은 유사배열객체 이면서 이터러블 이다.
    • 유사 배열 객체였고, ES6 에 도입되면서 Symbol.iterator 메서드를 구현하여 이터러블이 되었습니다.
  • 유사배열객체는 Array.from 메서드를 통해서 배열로 형변환할 수 있다.
    • 그렇지만 스프레드문법(...) 은 이터러블 에만 사용 가능합니다.

for문, for-in, for-of 말고, forEach 는?

  • Set 은 forEach 는 되는데 map, filter 같은건 안돼요.
  • String 은 forEach 가 안돼요.
  • NodeList 는 forEach 가 돼요.

되는 것은 각각 자료구조 prototype 에 존재하는 메서드 이기 때문에 되는 것입니다.

  • 배열에서 흔히 쓰여지는 메서드라고 해서 혼동하지 말아야 합니다.

정리

  • Array.from 은 유사배열객체 와 이터러블 을 배열로 만들어 주는 메서드이다.
  • 스프레드문법(...) 은 이터러블 만을 배열로 형변환 시켜주고, 유사배열객체 는 사용할 수 없다.
  • Array, String, Map, Set, HTMLCollection, NodeList 등은 이터러블 이며 이터러블 이면서 유사배열객체 인 자료구조도 존재한다. (Array, HTMLCollection, String 등)
  • for-of이터러블의 값을 순회하면서 반환한다.
  • for-in 은 프로토타입 체인상에 존재하는 모든 프로토타입의 프로퍼티 중에서 속성값 enumerable 가 true 인 값을 반환한다.
  • forEach 는 해당 자료구조의 prototype 에 존재하는 메서드이기 때문에 사용이 되는 것이다.
profile

파크로그

@파크park

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!