Как 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();
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
Итак, 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>