[20211226] 유사배열

유사배열(혹은 유사배열 객체)?

배열이 아닌데 배열처럼 사용할 수 있는 것.

객체의 key가 숫자고, length라는 속성을 가지고 있을 때

유사배열 예시

// 직접 만든 유사배열
const yoosa = {
  0: "a",
  1: "b",
  2: "c",
  length: 3,
};

// 흔히 사용하는 유사배열
const nodes = document.querySelectorAll("div"); // NodeList [div, div, div, ...]
const elements = document.body.children; // HTMLCollection [nonscript, div, script, ...]

function foo() {
  arguments; // foo(1, 2, 3)로 호출된 경우를 가정, Arguments [1, 2, 3, ...]
}

위 예시 모두 배열처럼 사용할 수 있지만 배열이 아닌 유사배열이다.

유사배열 판별

  1. Array.isArray()
  2. instanceof
const nodes = document.querySelectorAll("div");

Array.isArray([]); // true
Array.isArray(nodes); // false

[] instanceof Array; // true
nodes instanceof Array; // false

배열과 유사배열의 차이

유사배열도 배열처럼 특정 인덱스에 접근할 수 있고, 길이도 알 수 있다.

하지만, 배열 메서드를 사용하지 못한다.

즉, Array.prototype을 사용할 수 없다.

// NodeList의 상위 프로토타입는 Object (NodeList -> Object)
const nodes = document.querySelectorAll("div");

// HTMLCollection의 상위 프로토타입도 Object (HTMLCollection -> Object)
const elements = document.body.children;

질문) NodeList 사용할 때, forEach 쓸 수 있던데요?? forEach 그거 Array.prototype에만 있는거 아닌가요??

const nodes = document.querySelectorAll("div");
nodes.forEach(console.log); // 모든 div 출력

NodeList의 상위 프로토타입은 Object이다.

그래서 여기서 사용된 forEach는 Array.prototype.forEach일 수 없다.

왜냐아면, NodeList 프로토타입 체인에 Array가 없기 때문.

정답은 NodeList.prototype에서 Array.prototype.forEach와 동일한 메서드를 제공하고 있다.

유사배열에서 push나 reduce와 같이 배열의 다른 모든 메서드는 사용할 수 없다.

사용할 수 있는 메서드는 유사배열 객체에서 제공해주는 메서드이다.

유사배열로 배열 메소드 사용하기

  1. call, apply 사용
const nodes = document.querySelectorAll("div");

// Function.prototype.call(thisArg, func...)
Array.prototype.forEach.call(nodes, (el) => console.log(el));
[].forEach.call(nodes, (el) => console.log(el));
  1. 유사배열을 배열로 변경
  • slice()
    • slice함수에 인자가 없으면 0번 인덱스부터 length까지 배열을 자른다.
    • 즉, 인자가 없으면 똑같은 배열을 반환
      Array.prototype.slice.call(유사배열);
      
  • Array.from() → ES6부터

    MDN Web docs : The Array.from() static method creates a new, shallow-copied Array instance from an array-like or iterable object.

    Array.from(유사배열);
    
  • Spread operator (…) → ES6부터

    • iterables를 array로 변경 가능하다.
    [...유사배열]
    
    nodeList.map(...) // undefined
    [...nodeList].map(...) // 정상적으로 map 함수 실행
    

결론

[ ]로 감싸져 있다고 해서 모두 배열이 아니다.


출처

카일 심슨 저자, YOU DON’T KNOW JS 타입과 문법, 스코프와 클로저 편
(JavaScript) 배열과 유사배열