Показать сообщение отдельно
  #1 (permalink)  
Старый 04.02.2010, 05:21
Аватар для x-yuri
Отправить личное сообщение для x-yuri Посмотреть профиль Найти все сообщения от x-yuri
 
Регистрация: 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;
    
    ...
}
Ответить с цитированием