var array = [1, 2, 3];
array; // [1, 2, 3]
var nodes = document.querySelectorAll('div'); // NodeList [div, div, div, div, div, ...]
var els = document.body.children; // HTMLCollection [noscript, link, div, script, ...]

nodes와 els는 프론트엔드 개발을 하다보면 많이 접하는 친구들이다.

위 예제에서 array는 배열이고, nodes와 els는 유사 배열이다.

겉만 봐서는 둘의 차이가 없다. Array.isArray 메서드를 사용해서 뭐가 배열 인지 확인 해보자.

console.log(Array.isArray(array)); // true
console.log(Array.isArray(nodes)); // false
console.log(Array.isArray(els)); // false

직접 배열 리터럴로 선언한 array만 배열이다. 비슷한 방법으로, array instanceof Array로도 판단할 수 있다.

var yoosa = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

위와 같은 객체가 유사 배열이다. 키가 숫자고, length라는 속성을 가지고 있다. 배열도 객체 라는 성질을 이용한 트릭이다. 배열처럼 yoosa[0], yoosa[1], yoosa.length같은 것을 모두 활용할 수 있다.

배열과 유사 배열을 구분해야 하는 이유는, 유사 배열의 경우 배열의 메서드를 쓸 수 없기 때문이다.

array.forEach(function(el) { console.log(el); }); // 1, 2, 3
els.forEach(function(el) { console.log(el); }); // Uncaught TypeError: els.forEach is not a function

els에 forEach같은 배열 메서드를 사용하면 에러가 발생한다. (nodes는 프로토타입에 forEach가 있어서 된다.) 배열이 아니므로 발생하는 것이다.

이럴 때 메서드를 빌려 쓰는 방법이 있다. call, apply, from을 이용하는 방법이다.

Array.prototype.forEach.call(nodes, function(el) { console.log(el); });
[].forEach.call(els, function(el) { console.log(el); });
Array.from(nodes).forEach(function(el) { console.log(el) });

...spread 문법으로 유사객체를 배열로 바꿀수 있다!

const foo = document.querySelectorAll('.foo');

// good
const nodes = Array.from(foo);

// best
const nodes = [...foo];