유사배열 객체만들기 (making array-like object in JS)

평소에는 jQuery를 잘 쓰지 않는다. 이미 jindo를 이용해 원하는 형태로 얼마든지 개발할수있기 때문이다. 하나의 라이브러리를 딥하게 쓰다보면, 그 라이브러리에 매우 익숙하게 되고 무엇이 문제인지 무엇이 좋은지 잘 모를때가 있다. 그래서 종종 개인 프로젝트에는 jQuery를 일부러라도 찾아서 쓴다.

몇일전 부터 jQuery를 조금 딥하게 보고 있다가 특이한 점을 발견했다.
바로 jQuery() 객체로 인스턴스를 만들면 다음과 같이 배열로 반환한다는 점이다.

> jQuery()
[]

> jQuery('#test')
[<div id=​"test" class=​"this is a class">​OK​</div>​]

하지만 겉모습만 배열이지 실제로는 jQuery의 인스턴스다.

jQuery() instanceof Array  // false
jQuery() instanceof jQuery // true

오~! 신기한데 어떻게 저럴수 있지? 겉모습은 배열이지만 배열 아닌 유사배열!! 자바스크립트에서 내가 아는 유사배열은 두가지다!. 하나는 arguments 객체고 다른 하나는 셀렉터가 반환하는 NodeList다. 유사배열은 배열이 가지고 있는 배열 특유의 메소드들이 없다. 그래서 어떻게 보면 불편할수도 있다.

그런데 jQuery와 같이 인스턴스가 유사배열 형태로 반환이 되면 장점이 있다. 다음과 같이 DOM을 수정하면 바로 디버거 콘솔창에서 변화를 확인해볼수있다는 사실이다!!

> jQuery('<div>').addClass('test')
[<div class=​"test">​</div>​]

확인해본 결과 웹킷계열의 디버거에서만 위와같이 찍히고, 다른 브라우저들은 그냥 객체로 나온다. 그래도 개발은 크롬에서 주로 하기 때문에 위와같이 콘솔로그만 찍혀도 엄청 도움이 된다.

그래서 만들어봤다. 어떻게 저렇게 하면 나올수있을까?
혹시나 싶어 배열을 프로토타입 상속으로 만들어봤다.

var fine = function(){ };
fine.prototype = Array.prototype;
var x = new fine();

x instanceof fine;  // true
x instanceof Array; // true

그런데 배열을 프로토타입 상속으로 받으면 x는 배열의 인스턴스도 된다. 뭐 누구의 인스턴스가 되냐 안되냐가 그렇게 중요한 문제는 아니지만, 여기서 해당 인스턴스가 되면 필요없는 배열의 메소드들이 fine 객체에 들어갈수가 있다는 것이 문제가 된다.

그럼 어떻게 해야하나? 결론부터 말하면 만들고 싶은 객체에 length와 splice 프로퍼티를 넣으면된다.

> var foo = {length:0, splice:function(){}, x:1, y:1};
> console.log(foo);
[splice: function, x: 1, y: 1]

이런 트릭이 있었다니..대박!! 물론 웹킷계열의 디버거에서만 저렇게 나온다. 요거때메 몇일을 궁리하다가 검색으로 찾아냈는데… 사실은 jQuery 코드에도 위와 같은 코드가 숨겨져 있었다!!

// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push : core_push,
sort : [].sort,
splice : [].splice

꼼꼼한 레식사마~ ㅋ