04.02.2010, 05:21
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
читабельность, какая она бывает
1) transparency of implementation
/**
* Выбирает элементы по селектору selector с дополнительными параметрами.
* @param {String} selector Строка формата, соответствующего формату аргумента функции
* {@link #createSelectorFilter}. Кроме того, первым символом возможно указать !, тогда будет возвращен
* только первый элемент, после нахождения которого поиск будет прекращен.
* @param {Object} options Объект с параметрами. Все параметры необязательны.
* <ul>
* <li>parent -- элемент, внутри которого осуществлять поиск. По умолчанию поиск ведется по всему текущему
* документу.</li>
* <li>filter -- функция, вызываемая для каждого найденного по селектору элемента. Если определена, то в
* результирующую выборку попадают только те элементы, для которых она вернет истинное значение.
* При вызове передается элемент и его индекс среди всех найденных по тегу.</li>
* <li>map -- функция, вызываемая для каждого найденного по селектору элемента и удовлетворяющего функции
* filter, если та определена. В результирующий массив попадают результаты работы этой функции.
* При вызове передается элемент и его индекс среди прошедших все фильтры.</li>
* <li>reduce -- функция, агрегирующая данные из разных элементов. При вызове передаются агрегированное
* значение, сам элемент (или результат работы map) и его индекс среди отфильтрованных элементов.
* Если параметр reduceInit не указан, то для первого найденного элемента reduce не вызывается.</li>
* <li>reduceInit -- инициализирующее значение для reduce. Если не задано, то подставляется первый найденный
* элемент или результат работы map, если она задана.</li>
* <li>scope -- контекст, внутри которого вызываются функции filter, map и reduce.</li>
* </ul>
* Если в селекторе указан модификатор !, то функции map и reduce вызваны не будут, а будет возвращен найденный
* элемент.
* @return {Mixed} В зависимости от переданных параметров.
*/
function $$(selector, options) {
options = options || {};
var single = false;
if (selector.indexOf('!') === 0) {
single = true;
selector = selector.substr(1);
}
var selectorFilter = createSelectorFilter(selector);
var filter = function(element, index) {
if (!selectorFilter(element)) {
return false;
}
return typeof options.filter == 'function' ? options.filter.call(options.scope, element, index) : true;
};
var elements = $(options.parent || document).getElementsByTagName(selector.split('.')[0] || '*');
var result = [], reduceValue = options.reduceInit, reduceInitialized = ('reduceInit' in options);
for (var i = 0, j = 0; i < elements.length; i++) {
var el = elements[i];
if (filter(el, i)) {
if (single) {
return el;
}
var value = typeof options.map == 'function' ? options.map.call(options.scope, el, j++) : el;
if (typeof options.reduce == 'function') {
reduceValue = reduceInitialized ? options.reduce.call(options.scope, reduceValue, value, j++) : value;
reduceInitialized = true;
} else {
result.push(value);
}
}
}
return typeof options.reduce == 'function' ? reduceValue : result;
}
/**
* Создает функцию, проверяющую соответствие передаваемых dom-элементов селектору.
* @param {String} selector Строка вида 'tagName.className1.className2.className3'. tagName опционален, если
* не указан, будет подставлена *. Если указано несколько className, то выберутся элементы, которые содержат
* их все.
* @return {Function}
*/
function createSelectorFilter(selector) {
var selectorParts = selector.split('.'), classNameRegexps = [], tagName = selectorParts[0].toUpperCase() || '*';
for (var i = 1; i < selectorParts.length; i++) {
classNameRegexps.push(new RegExp('(^|\\s)' + selectorParts[i] + '(\\s|$)'));
}
return function(el) {
if (tagName != '*' && el.tagName != tagName) {
return false;
}
for (var i = 0; i < classNameRegexps.length; i++) {
if (!classNameRegexps[i].test(el.className)) {
return false;
}
}
return true;
};
}
2) transparency of intentions
function $$( selector, options ) {
options = options || {};
var s = parseSelector( selector );
var els = getElsByTag( s.tag, options );
var r = [];
var reduceValue = reduceInit( els, options );
for( var i=0, j=0; i<els.length; i++ ) {
var el = els[i];
if( filter(el, i) ) {
if( selector.single )
return el;
if( options.map )
el = map( options, el, j );
r.push( el );
if( mustCallReduce(options, i) )
reduceValue = reduce( options, reduceValue, el, j );
j++;
}
}
if( options.reduce )
return reduceValue;
else
return r;
function getElsByTag( tag, options ) {
var parent = options.parent || document;
return parent.getElementsByTagName( tag || '*' );
}
function filter( el, i, s ) {
if( ! filter.selectorFilter )
filter.selectorFilter = createSelectorFilter( s );
if( ! filter.selectorFilter(el) )
return false;
return options.filter ? options.filter.call( options.scope, el, i )
: true;
}
function map( options ) {
var args = toArray(arguments).slice(1);
return options.map.apply( options.scope, args );
}
function reduce( options ) {
var args = toArray(arguments).slice(1);
return options.reduce.apply( options.scope, args );
}
function reduceInit( els, options ) {
return 'reduceInit' in options ? options.reduceInit
: els[0];
}
function mustCallReduce( options, i ) {
return !( i == 0 &&
!( 'reduceInit' in options ));
}
}
function createSelectorFilter( selector ) {
if( typeof selector != 'object' )
selector = parseSelector( selector );
var classNameRegexps = [];
for( var i=0; i<selector.classNames.length; i++ ) {
var r = '(^|\\s)' + selectorParts[i] + '(\\s|$)';
classNameRegexps.push( new RegExp(r) );
}
return function() {
if( selector.tagName != '*' &&
el.tagName != selector.tagName )
return false;
for( var i=0; i<classNameRegexps.length; i++ ) {
if( ! classNameRegexps[i].test(el.className) )
return false;
return true;
};
}
function parseSelector( s ) {
if( s.indexOf('!') === 0 ) {
var single = true;
s = s.substr( 1 );
}
var els = s.split('.');
return { tag: els[0] && els[0].toUpperCase(),
classNames: els.slice(1),
single: single };
}
или даже так
Array.prototype.each = function( f ) {
for( var i=0; i<this.length; i++ )
f.call(this, this[i], i);
}
Array.prototype.filter = function( f ) {
if( ! f )
return this;
var r = [];
for( var i=0; i<this.length; i++ )
if( f.call(this, this[i], i) )
r.push( this[i] );
return r;
}
Array.prototype.map = function( f, scope ) {
if( ! f )
return this;
if( ! scope )
scope = this;
var r = [];
for( var i=0; i<this.length; i++ ) {
var v = f.call(scope, this[i], i)
if( v instanceof Array )
r = r.concat( v );
else if( v )
r.push( v );
}
return r;
}
function $$( selector, options ) {
options = options || {};
var s = parseSelector( selector );
var els = getElsByTag( s.tag, options ).filter( filter )
.map( options.map, options.scope );
if( ! options.reduce )
return s.single ? els[0]
: els;
var r = reduceInit( els, options );
els.each(function( el, i ) {
if( mustCallReduce(options, i) )
r = reduce( options, r, el, i );
});
return r;
...
}
|
|
04.02.2010, 06:27
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
ненавижу, когда скобочки изнутри пробелами отбивают
|
|
04.02.2010, 13:09
|
Новичок на форуме
|
|
Регистрация: 19.02.2008
Сообщений: 9,177
|
|
x-yuri,
лучше все-таки словами, а то ведь непонятно, что сказать хотел. По поводу "или даже так": я бы с удовольствием заюзал методы forEach/filter/... (ты, кстати, неправильно их реализацию написал), но их в IE нет, а подгружать Array.js неохота из-за этой функции. В прикладном коде у меня зачастую циклов for вообще нет
|
|
04.02.2010, 15:23
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
ну эту тему я создал, чтобы обсудить, какая бывает читабельность
Сообщение от Kolyaj
|
ты, кстати, неправильно их реализацию написал
|
а что, кстати, не так?
|
|
04.02.2010, 15:30
|
Новичок на форуме
|
|
Регистрация: 19.02.2008
Сообщений: 9,177
|
|
Сообщение от x-yuri
|
а что, кстати, не так?
|
http://alljs.ru/2009/08/rasshirenie-...ruktora-array/
Цитата:
|
Все эти функции перебирают значения массива, в отличие от стандартных ECMA-методов, которые или не перебирают ничего, или перебирают индексы. Это означает, что обычным циклом for при переборе отделаться не удастся, необходимо проверять, существует ли данный индекс в массиве или он пропущен.
|
|
|
04.02.2010, 15:45
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
Сообщение от Kolyaj
|
Все эти функции перебирают значения массива, в отличие от стандартных ECMA-методов, которые или не перебирают ничего, или перебирают индексы. Это означает, что обычным циклом for при переборе отделаться не удастся, необходимо проверять, существует ли данный индекс в массиве или он пропущен.
|
а, ну я просто рассчитывал на "упрощенную модель массива", без пропущенных индексов. Пока что пропускать индексы не нужно было. Если нужны пропущенные индексы, думаю, лучше объект использовать или как-нибудь обойтись без этих функций
|
|
04.02.2010, 15:55
|
Новичок на форуме
|
|
Регистрация: 19.02.2008
Сообщений: 9,177
|
|
Проблема в том, что ты стандартные функции подменяешь.
|
|
05.02.2010, 06:40
|
|
Рассеянный профессор
|
|
Регистрация: 06.04.2009
Сообщений: 2,379
|
|
Зачем в методах массивов работать с ними как с объектами? Давно пытаюсь понять какой в этом смысл, но, похоже, это просто модно так.
|
|
05.02.2010, 08:58
|
Новичок на форуме
|
|
Регистрация: 19.02.2008
Сообщений: 9,177
|
|
Сообщение от Riim
|
Зачем в методах массивов работать с ними как с объектами?
|
Ты имеешь в виду зачем вообще использовать методы forEach/map/filter/reduce/...?
|
|
05.02.2010, 09:18
|
|
Рассеянный профессор
|
|
Регистрация: 06.04.2009
Сообщений: 2,379
|
|
Сообщение от Kolyaj
|
Ты имеешь в виду зачем вообще использовать методы forEach/map/filter/reduce/...?
|
я имею ввиду почему вот в этом случае:
var arr = [];
arr[5] = 'TEXT';
alert(
arr.reduce(function(a, n) {
return a + n;
}, '')
);
получается 'TEXT' а не 'undefinedundefinedundefinedundefinedundefinedTEXT ' ?
|
|
|
|