Как jQuery создает массив-объекты?

сначала я думал, что это просто назначение obj[0], obj[1], obj[2] и т. д. к объекту jQuery перед возвращением его, и что длина вручную назначена. Но нет, с консоли.log регистрирует массив, а не объект.

я быстро взглянул на источник jQuery, но поскольку я не знаком с ним, я не легко его взломал. jQuery.makeArray выскочил первым, но оказалось, что это противоположно тому, что я ищу, вы фактически теряете методы объекта, используя он.

моя первая догадка сначала инициирует массив, а затем копирует все свойства и методы из объекта в него.

есть ли у кого-нибудь с опытом исходного кода jQuery четкий ответ на это?

3 ответов


это так же просто, как создать новую функцию и назначить ей новый массив в качестве ее прототипа.

function ArrayLike() {}
ArrayLike.prototype = [];

например:

function ArrayLike() {}
ArrayLike.prototype = [];
ArrayLike.prototype.fromArray = function(arr) {
    for(var i = 0; i < arr.length; i++)
        this.push(arr[i]);
};
ArrayLike.prototype.foo = function() {
    console.log("foo", this);
};

var a = new ArrayLike();
a.fromArray([1, 2]);
console.log(a);
a.foo();

http://jsfiddle.net/Xeon06/fUgaf/


jQuery и создает то, что называется (в стандартной Эс) массив-как объекты. В частности, у них есть свойство length, а также (которое использует свойство length) у них есть соответствующие элементы, помещенные под целочисленные индексы.

все просто:

var arrayLike = {length: 3, 0:'first', 1:'second', 2:'third'};
Array.prototype.join.call(arrayLike, '|'); // "first|second|third"

если вы посмотрите на стандартный (15.4.4), в частности Array.prototype методы вы увидите следующее примечание под каждым из них:

Примечание * функция преднамеренно является общей; она не требует, чтобы его это значение является объектом Array. Поэтому его можно перенести к другие виды объектов для использования в качестве метода. Может ли функция * быть успешно примененным к объекту хоста зависит от реализации.

если вы посмотрите на описание этих алгоритмов, они в значительной степени просто использовать свойство length для прохода на объект (в нашем случае массив-как object) и получить доступ к значения за этими целочисленными ключами. По этой причине они являются общими и работают с обычными объектами js с соответствующими свойствами.

по сути, это все, что делает jQuery (если я правильно помню, что здание сделано в sizzle, движке селектора css jquery). Вы можете проверить это/доказать это различными способами. Например, стандартный массив js выполняет некоторую магию при укорочении свойства length, но объект jquery не:

var arr = [1,2,3], $arr = $('div'); // assuming three divs
arr.length=2; arr[2]; // undefined
$arr.length=2; $arr[2]; // still references the div

http://jsfiddle.net/cnkB9/

Итак, jQuery makeArray преобразует объект, подобный массиву jQuery, и превращает его в фактический собственный массив js, но, как вы сказали, тогда он не имеет всех методов jQuery.

Что касается того, почему он появляется в консоли, я ссылаюсь на этот отличный ответ:что заставляет Firebug / Chrome console рассматривать пользовательский объект как массив? это объясняет, что присутствие length собственность и splice функция позволяет ему отображаться как массив в большинство консолей. Как уже упоминалось, это не так в веб-консоли FF4, которая просто показывает вам, что это не собственный массив js. Интересно, что splice функция на самом деле не должна работать, просто присутствовать и быть функцией. Например:

>> console.log({length:2,0:'some',1:'thing',splice:new Function});
["some", "thing"]

следующий простой код преобразования массива, возвращаемого селектором jQuery при работе с собственным массивом JavaScript:

var $arr = $('option'); // assuming three options
var arr = Array.apply(null, $arr);
// arr contains [option, option]

учитывая следующий HTML для кода выше:

<html>
    <head>
        <script type="text/javascript" src="jquery-1.5.2.min.js"></script>
    </head>
    <body>
        <select id="addSelection">
            <option value="Original Value">Original Value</option>
            <option value="Two Value">Value two</option>
            <option value="Three Value">Value three</option>
        </select>

        <br />

        <input id="btn" type="button" value="Changes" />
    </body>
</hmtl>