내가 보려고 만든 개발 공부 일지

자바스크립트 - 배열, 이터러블, 유사배열 본문

Javascript

자바스크립트 - 배열, 이터러블, 유사배열

kwangsunny 2022. 2. 25. 01:35

자바스크립트 공부를 하면서 유사배열이 어쩌고 이터러블이 어쩌고 하는 말을 종종 들었었다.

그런데 실제로 개발할땐 그냥 배열만 잘 다룰줄 알아도 전혀 문제가 없었기에 굳이 찾아보거나 하진 않았다.

그래서 이번에 배열, 이터러블, 유사배열 이 세가지 녀석들에 대해 공부한 내용을 정리해 본다.

 

 

1. 배열 

배열은 우리에게 너무나도 친숙한 녀석이다.

배열은 자바스크립트 내장 함수인 Array에 의해 만들어지는 객체 타입의 자료형이다.

let a = new Array(); // (1)
let b = []; // (2)

배열 선언은 위와 같이 할 수 있는데, (1) 과 (2) 는 같은 뜻이다.

(1)을 단축해서 쓴것이 (2)이다.

 

new 키워드를 통해 반환된 값이기 때문에 당연히 배열의 타입도 객체이다.

let a = [];
typeof a === 'object' // true

typeof 를 해보면 위와 같이 object 인걸 확인할 수 있다.

종종 진짜 객체와 배열을 구분해야 할 때가 있는데, 이땐 Array 내장 메서드인 isArray 나 instanceof 를 이용하여

타입을 구분할 수 있다.

let arr = []; 
let obj = {};

Array.isArray(arr); // true
Array.isArray(obj); // false

arr instanceof Array; // true
obj instanceof Array; // false

위에서 말했듯이 배열은 Array 함수에 new 키워드를 붙여서 만들어진 인스턴스이다.

그래서 Array 함수의 prototype 객체에 구현되어있는 다양한 내장 메서드들을 마치 상속받듯이 그대로 사용할 수 있다.

 

여기서 prototype 객체 라는건 갑자기 뭐지..? 라고 생각이 들것이다.

자바스크립트에서 함수를 선언하면 그 함수는 prototype 이라는 객체를 프로퍼티로 가지게 된다.

그리고 이 protoype 객체는 new 를 통해 만들어질 수많은 인스턴스들이 공유하게 될 객체 라고 생각하면 된다.

 

실제로 Array 함수의 prototype 을 보면 아래와 같이 생겼다.

내장함수 Array 의 프로토타입

그래서 배열에 점(.) 만 찍어도 우리가 따로 구현한 적도 없는 filter, map, find 같은 메서드들을 사용할 수 있는 것이다.

( 프로토타입은 나중에 따로 다시 정리해야겠다! ) 

 

 

2. 이터러블

지금까지 배열에 대해 간단히 정리해보았고, 이제 이터러블을 살펴보자.

iterable, 반복 가능하다는 뜻인데, 반복 가능하다는건 for 문법을 사용할 수 있다는 뜻이겠쥬 ?

 

A. for(let i=0; i<len; i++)

B. for ... of

C. for ... in

 

위에 나열한 세가지 for문을 간단히 설명하면

A는 기본적인 형태의 반복문 이고,

B는 배열에 사용하는 반복문 ,

C는 객체 프로퍼티를 나열(enumarable) 할때 사용하는 반복문이다.

 

여기서 우리가 주목할 녀석은 B, for ... of 이다.

for...of 는 배열에 사용하는 반복문이라 했는데 더 정확히 말하면 이터러블 객체에만 사용할 수 있는 반복문이다.

 

이터러블 객체란 Symbol.iterator 메서드를 가지고있는 객체를 말한다.

여기서 Symbol.iterator 는 자바스크립트에 내장된 심볼값이다.

심볼은 숫자나 문자열 같은 원시타입 중 하나인데 유일한 값을 만들고 싶을때 사용하는 타입이다.

아무튼, 배열이 아닌 일반적인 객체에도 Symbol.iterator 메서드를 추가해 주면 그 객체도 

for...of 를 사용할 수 있다.

for...of 는 처음 호출될때 해당 객체가 Symbol.iterator를 키로 하는 메서드가 있는지를 먼저 찾는데

있다면 메서드를 실행하고 없으면 에러가 발생한다.

 

Symbol.iterator 메서드는 next() 메서드를 가지고있는 객체를 반환해야하고

이 반환된 객체를 이터레이터(iterator) 라고 부른다. (반드시 next 라는 이름을 사용해야 한다.)

그리고 이 이터레이터는 {done : boolean, value : any} 와 같은 형태의 객체를 반환해야 한다.

done 값이 true면 for...of 는 종료되고 false면 value 값이 저장된다.

 

설명이 복잡한데... 지금까지 설명을 토대로 작성한 예시 코드를 보자. 

// 일반적인 객체 선언
let obj = {
    count: 5
}

// Symbol.iterator 메서드 구현
obj[Symbol.iterator] = function(){ // (A)
    // 이터레이터 반환
    return {
        cnt: 1,
        end: this.count,
        next: function(){ // (B)
            if(this.cnt <= this.end){
                return {
                    done: false,
                    value: this.cnt++
                }
            } else {
                return {
                    done: true,
                    value: null
                }
            }
        }
    }
}

// obj를 for...of로 실행
for(let val of obj){ // (C)
    console.log(val); // 출력: 1 2 3 4 5
}

위 예시는 일반객체 obj 를 선언하고 Symbol.iterator 메서드를 구현하여 이터러블 하도록 만들어

for...of 반복문을 사용할 수 있게 만든 후 obj.count 값만큼 1부터 숫자를 출력하도록 해주는 코드이다.

 

만약 바로 obj를 for...of 로 실행하면 에러가 났을 것이다.

(C) 부분에서 for...of 는 제일 먼저 obj 가 Symbol.iterator를 가지고 있는지 찾는다.
(A) 같이 객체에 Symbol을 키로 등록할땐 대괄호 문법을 사용해야한다. (점(.) 방식은 에러 발생함)

(B) 와 같이 Symbol.iterator 메서드는 next 메서드를 가지고있는 객체를 반환해야하고 next 메서드는

{done, value} 형태의 객체를 반환해야 하는데, done === true 가 될때 까지 for...of 는 next() 를 실행한다.

 

앞서 말했듯이 for...of 는 이터러블 객체에만 사용할 수 있다.

그래서 위 예시와 같은 일반객체는 Symbol.iterator 메서드를 직접 구현해줘야 for...of 를 사용할 수 있게 되는것이다.

 

그런데 배열과 문자열, 이 두놈도 for...of 를 사용 가능하다.

이말은 뭐다? 배열과 문자열에도 Symbol.iterator 메서드가 구현되어 있겠구나 라는걸 의미한다.

그럼 어디에 구현되어 있을까? 바로 나를 만든 생성자 함수의 프로토타입에 이미 구현되어있다.

 

실제로 [ ].__proto__ 와 "".__proto__ 를 콘솔에서 실행하면 해당 생성자 함수의 프로토타입을 참조할 수 있는데

Array 함수와 String 함수의 prototype 객체에 모두 Symbol.iterator 메서드가 존재하는 것을 확인할 수 있다.

( obj.__proto__ 는 new SomeFunc() 를 통해 만들어진 obj가 SomeFunc의 prototype 객체를 참조할 수 있고, 이를 프로토타입 체인이라 불린다.)

좌 : [].__proto__ 실행  /  우 : "".__proto__ 실행

 

 

3. 유사배열

마지막으로 유사배열에 대해 알아보자.

이름 그대로 배열은 아닌데 배열 비슷하게 생긴 객체를 유사배열이라 한다.

 

아래 두 조건을 충족하면 유사배열 이라고 부른다.

1. 키값이 정수로 이루어져 있다.

2. legnth 프로퍼티를 가지고 있다.

let notRealArray = {
    0: '유사',
    1: '배열',
    length: 2,
}

위 예시가 바로 유사배열이다.

배열처럼 생겼지만 진짜 배열은 아니기에 filter, map 같은 내장 메서드를 쓸 수 없다.

 

다음은 실제 유사배열 객체의 몇가지 예시다.

1. 문자열은 정수값을 인덱스로 쓰고있고 legnth 프로퍼티도 가지고있다. 

   그래서 문자열은 이터러블 이면서 유사배열이다.

 

2. 함수가 호출됐을때 전달받은 모든 인자값들을 담고있는 arguments 객체도 유사배열이다.

 

3. document.querySelectorAll(셀렉터) 로 받아온 노드 리스트도 정수 키값에 length 를 가지고있는 유사배열이다.  

 

그런데 이런 유사배열 객체를 진짜 배열 다루듯 filter, find와 같은 내장 메서드를 쓰고싶은 경우가 많을 것이다.

이때 사용 가능한 방법이 Array.from 을 이용하는 것이다.

Array.from( arr ) 은 arr 가 유사배열 객체 or 이터러블 객체 라면 arr를 진짜 배열로 만들어 반환해준다.

 

 

 

잘못된 내용이 있다면 댓글 남겨 주세요 :)

 

 

 

 

Comments