Вход

Просмотр полной версии : Возвращаемый массив jQuery селектора


HelpeR
25.06.2012, 20:03
Здравствуйте всем!
Пытаюсь написать свой фрэймворк для опыта. Образец jQuery.
Застрял на моменте когда селектор jQuery возвращает массив с dom елементом и так же сам jQuery объект. Начал отслеживать историю развития фрэймворка, оказалось что только в версии http://code.jquery.com/jquery-1.4.1-vsdoc.js они реализовали эту фичу. Честно говоря не смог разобраться как это они реализовали.

Заранее спасибо!

devote
25.06.2012, 20:13
function jQuery() {
return new jQuery.prototype.init( arguments );
}

jQuery.prototype = {
constructor: jQuery,
init: function( selector, context ) {
this[0] = "lala";
this[1] = "tratata";
this.length = 2;
return this;
},
length: 0
}

jQuery.prototype.init.prototype = jQuery.prototype;

alert( JSON.stringify( jQuery() ) );

HelpeR
25.06.2012, 20:26
devote, вы возможно не поняли вопроса. Этот момент я уже реализовал для себя, у меня так же возвращается объект. А вы попробуйте выполнить $('#someID') в консоле ФФ, то увидите что в результате появится не объект, а массив с dom элементом [div#someID]. Я дебажил и выводил ссылку на объект this до ретурна, она уже в тот момент ссылалась на пустой массив.

devote
25.06.2012, 20:30
хотя правильнее показать на таком примере:
function jQuery( selector, context ) {
return new jQuery.prototype.init( selector, context );
}

jQuery.prototype = {
constructor: jQuery,
init: function( selector, context ) {

context = context || document;

return jQuery.makeArray( this, context.querySelectorAll( selector ) );
},
length: 0
}

jQuery.makeArray = function( array, obj ) {

var length = obj.length;

for( var i = 0; i < length; i++ ) {
array[ i ] = obj[ i ];
}
array.length = length;

return array;
}

jQuery.prototype.init.prototype = jQuery.prototype;

var s = "", jq = jQuery('*');
for( var k in jq ) {
s += k + ": " + jq[k] + "\n";
}
alert( s );

HelpeR
25.06.2012, 20:38
Да с этим момент, я тоже разобрался. А вы можете ответить на счет этого моментаfunction jQuery( selector, context ) {
return new jQuery.prototype.init( selector, context );
}

jQuery.prototype = {
constructor: jQuery,
init: function( selector, context ) {
if ( !selector ) {
console.log(this); // [] как???
return this;
}

context = context || document;

return jQuery.makeArray( this, context.querySelectorAll( selector ) );
},
length: 0
}

jQuery.makeArray = function( array, obj ) {

var length = obj.length;

for( var i = 0; i < length; i++ ) {
array[ i ] = obj[ i ];
}
array.length = length;

return array;
}

jQuery.prototype.init.prototype = jQuery.prototype;

devote
25.06.2012, 20:47
console.log(this); // [] как???
это в какой версии джуквери вы такое увидели? Насколько мне известно джуквери не выдает массив собою, он есть объект. Откуда там массив понять не могу.

Вот вам мини jQuery, и джуквери сделан именно по такому принципу.

<div>Первый дивак</div>
<div>Второй дивак</div>
<script>
(function( window, undefined ) {

function jQuery( selector, context ) {
return new jQuery.prototype.init( selector, context );
}

jQuery.prototype = {
constructor: jQuery,
init: function( selector, context ) {

context = context || document;

return jQuery.makeArray( this, context.querySelectorAll( selector ) );
},
length: 0
}

jQuery.prototype.init.prototype = jQuery.prototype;

jQuery.makeArray = function( array, obj ) {

var length = obj.length;

for( var i = 0; i < length; i++ ) {
array[ i ] = obj[ i ];
}
array.length = length;

return array;
}

jQuery.each = jQuery.prototype.each = function( object, callback ) {

if ( typeof object == 'function' ) {
callback = object;
object = this;
}

if ( object.length === undefined ) {
for( var i in object ) {
if ( callback.call( object[ i ], i, object[ i ] ) === false ) {
break;
}
}
} else {
for( var i = 0, j = ~~object.length; i < j; i++ ) {
if ( callback.call( object[ i ], i, object[ i ] ) === false ) {
break;
}
}
}

return this;
}

window.jQuery = jQuery;

})( window );


jQuery( "div:first-child" ).each(function() {
this.style.cssText = "width: 100px; height: 100px; background-color: #f00;";
});
</script>

DjDiablo
25.06.2012, 21:03
console.log(this); // [] как???
в какой это версии ?
в том примере кода который вы привили такое попросту невозможно

HelpeR
25.06.2012, 23:58
в какой это версии ?
это начинается с версии 1.4.1 и даже есть в ссылке которую я указал в первом посте.
А это был не мой пример, я только добавил часть кода которая есть в jQuery в код предложенные devote, что бы показать как написанно в jQuery.
этот пример с версии 1.7.2
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;

// Handle $(""), $(null), or $(undefined)
if ( !selector ) { // тут я решил проверить this. Можете сами протестить результа будет равен пустому массиву.
return this;
}

// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}

// The body element only exists once, optimize finding it
if ( selector === "body" && !context && document.body ) {
this.context = document;
this[0] = document.body;
this.selector = selector;
this.length = 1;
return this;
}
Т.е. говоря иначе. Если в консоле ФФ набрать $('#someID') то до версии 1.4.1 результат был jQuery{.....} а с версии 1.4.1 и выше результат [tag#someID]

Gvozd
26.06.2012, 00:41
[самокритика]
Тему не читай
Фигню отвечай
[/самокритика]

в jQuery сделано явно не так
Но этот пример также позволяет работать с объектом jQuery как с массивом, и цепочные вызовы
А вообще, что за вопросы - посмотрите как сделано в jQuery, благо его исходники открыты - и не морочьте голову людям

function jQuery(array) {
(array instanceof Array) ? [].push.apply(this, array) : [].push.call(this, array);
this.push = function() {
[].push.apply(this, arguments);
return this;
};
this.pop = function() {
return new jQuery([].pop.apply(this));
};
return this;
}
jQuery.prototype = [];


var a = new jQuery([1,2]);
console.log(a);
a = a.push(3,4).push(5,6);
console.log(a);
console.log(a.pop());

HelpeR
26.06.2012, 12:37
ок, спасибо всем за помощь.

devote
26.06.2012, 12:41
Т.е. говоря иначе. Если в консоле ФФ набрать $('#someID') то до версии 1.4.1 результат был jQuery{.....} а с версии 1.4.1 и выше результат [tag#someID]
да все очень просто, видимо более поздние джуквери доавляют метод toString который возвращает последний использованный селектор, тоесть примерно так:
function jQuery( selector, context ) {
return new jQuery.prototype.init( selector, context );
}

jQuery.prototype = {
constructor: jQuery,
init: function( selector, context ) {
this.selector = selector;
return this;
},
length: 0,
selector: "",
toString: function() {
return "[" + this.selector + "]"; // вот это ФФ наверно и считывает при выводе в консоль
}
}

jQuery.prototype.init.prototype = jQuery.prototype;

console.log( jQuery("#someID") );
alert( jQuery("#someID") );

melky
26.06.2012, 16:16
да все очень просто, видимо более поздние джуквери доавляют метод toString который возвращает последний использованный селектор

нет :p

<script src="http://code.jquery.com/jquery-1.7.2.js"></script>
<script>
alert( $().toString );
</script>


Честно говоря не смог разобраться как это они реализовали.

всё до безобразия просто :


=======================
Кратко
=======================


Смешивает список найденных элементов с текущим (возвращаемым) объектом jQuery.


Либо делает это через Array.push
Либо через простой проход (обычное копирование свойств с одного объекта\массива в другой)


К первому немного кода:

var current_jq = { };

var finded_elemenets = document.getElementsByTagName("div");

// т.к. nodelist - живой массив, замораживаем его, с помощью превращения в обычный массив.
finded_elements = [].slice.call(finded_elements, 0);

// переносим значения из результатов поиска в текущий экз.
// так работает Array.prototype.push
for(var i = 0; i < finded_elements.length; i++) {

// узкий момент. тут не по документации, я просто сократил несколько вызовов до одного.
if (!isNumeric(current_jq.length)) {
current_jq.length = +0;
}

current_jq = finded_elements[i];
current_jq.length++;
}

function isNumeric(variable) {
return !isNaN(parseFloat(variable, 10)) && isFinite(variable);
}

*к коду сверху*
С чего ты взял, что push работает именно так ?
Cм документацию (http://es5.javascript.ru/x15.4.html#x15.4.4.7). Если не понятно - могу разжевать.


=======================
Подробно :
=======================


$.fn.init (она вызывается при $(), знаем же?) вызывает поиск Sizzle (эта штучка ищет элементы по селектору, если кто не в курсе), указывая помимо всего аргумент с именем "extra" - это объект, куда Sizzle подмешает в конце результаты поиска.

Sizzle живёт в самой jQuery с именем find. (вызвать можно так : [I]jQuery.find(bla bla bla))

В конце своём сиззл вызывает функцию makeArray, передавая ей такие аргументы : первый - NodeList найденных элементов, второй - объект (тот самый extra).

её исходный код :

// array - найденные элементы
// results - jQuery объект, который будет возвращён.
var makeArray = function( array, results ) {
array = Array.prototype.slice.call( array, 0 );

if ( results ) {
results.push.apply( results, array );
return results;
}

return array;
};

что за метод push у объекта jQuery ? да самый обычный пример заимствования методов - т.е. это метод не самописный, а взятый у массивов.

<script src="http://code.jquery.com/jquery-1.7.2.js"></script>
<script>
alert( $().push );
</script>


можем убедиться в коварстве сиззла, подсунув ему что-нибудь своё :

<script src="http://code.jquery.com/jquery-1.7.2.js"></script>
<p>SAMPLE</p><div>LOL</div>
<script>
var extra = {
push: function(/*elements*/) {
for(var i = 0, args = arguments; i in args; i += 1) {
alert("Push добавляет : "+args[i]+"\n\nСтарая длина : "+this.length);
this[this.length++] = arguments[i];
}
},
length: 0,
context: document
}

jQuery.find("p, div", document, extra);
</script>




PS уморился.