Как я могу улучшить производительность моего текстового форматера JavaScript? - PullRequest
1 голос
/ 01 августа 2011

Я разрешаю своим пользователям переносить слова с помощью «*», «/», «_» и «-» как краткий способ указать, что они хотели бы выделять жирным шрифтом, курсивом, подчеркиванием или зачеркиванием текста. К сожалению, когда страница заполнена текстом с использованием этой разметки, я вижу заметное (допустимое на границе) замедление.

Вот JavaScript, который я написал для решения этой задачи. Можете ли вы дать отзыв о том, как я мог ускорить процесс?

function handleContentFormatting(content) {
    content = handleLineBreaks(content);

    var bold_object = {'regex': /\*(.|\n)+?\*/i, 'open': '<b>', 'close': '</b>'};
    var italic_object = {'regex': /\/(?!\D>|>)(.|\n)+?\//i, 'open': '<i>', 'close': '</i>'};
    var underline_object = {'regex': /\_(.|\n)+?\_/i, 'open': '<u>', 'close': '</u>'};
    var strikethrough_object = {'regex': /\-(.|\n)+?\-/i, 'open': '<del>', 'close': '</del>'};

    var format_objects = [bold_object, italic_object, underline_object, strikethrough_object];

    for( obj in format_objects ) {
        content = handleTextFormatIndicators(content, format_objects[obj]);
    }

    return content;
}

//@param obj --- an object with 3 properties:
//      1.) the regex to search with
//      2.) the opening HTML tag that will replace the opening format indicator
//      3.) the closing HTML tag that will replace the closing format indicator
function handleTextFormatIndicators(content, obj) {
    while(content.search(obj.regex) > -1) {
        var matches = content.match(obj.regex);
        if( matches && matches.length > 0) {
            var new_segment = obj.open + matches[0].slice(1,matches[0].length-1) + obj.close;
            content = content.replace(matches[0],new_segment);
        }
    }
    return content;
}

Ответы [ 3 ]

1 голос
/ 01 августа 2011
  1. Измените свое регулярное выражение с флагами /ig и удалите цикл while.

  2. Измените ваш цикл for(obj in format_objects) на обычный цикл for, поскольку format_objects - это массив.


Обновление

Хорошо, я нашел время, чтобы написать еще более быстрое и упрощенное решение, основанное на вашем коде:

function handleContentFormatting(content) {
    content = handleLineBreaks(content);

    var bold_object = {'regex': /\*([^*]+)\*/ig, 'replace': '<b>$1</b>'},
        italic_object = {'regex': /\/(?!\D>|>)([^\/]+)\//ig, 'replace': '<i>$1</i>'},
        underline_object = {'regex': /\_([^_]+)\_/ig, 'replace': '<u>$1</u>'},
        strikethrough_object = {'regex': /\-([^-]+)\-/ig, 'replace': '<del>$1</del>'};

    var format_objects = [bold_object, italic_object, underline_object, strikethrough_object],
        i = 0, foObjSize = format_objects.length;

    for( i; i < foObjSize; i++ ) {
        content = handleTextFormatIndicators(content, format_objects[i]);
    }

    return content;
}

//@param obj --- an object with 2 properties:
//      1.) the regex to search with
//      2.) the replace string
function handleTextFormatIndicators(content, obj) {
    return content.replace(obj.regex, obj.replace);
}

Вот демонстрационная версия .

Это будет работать с вложенными и / или не вложенными границами форматирования.Вы можете вообще опустить функцию handleTextFormatIndicators, если хотите, и выполнить замены внутри строки handleContentFormatting.

1 голос
/ 01 августа 2011

Вы можете делать такие вещи, как:

function formatText(text){
    return text.replace(
        /\*([^*]*)\*|\/([^\/]*)\/|_([^_]*)_|-([^-]*)-/gi,
        function(m, tb, ti, tu, ts){
            if(typeof(tb) != 'undefined')
                return '<b>' + formatText(tb) + '</b>';
            if(typeof(ti) != 'undefined')
                return '<i>' + formatText(ti) + '</i>';
            if(typeof(tu) != 'undefined')
                return '<u>' + formatText(tu) + '</u>';
            if(typeof(ts) != 'undefined')
                return '<del>' + formatText(ts) + '</del>';
            return 'ERR('+m+')';
        }
    );
}

Это будет хорошо работать на вложенных тегах, но не будет работать с перекрывающимися тегами, которые в любом случае недопустимы.

Пример на http://jsfiddle.net/m5Rju/

1 голос
/ 01 августа 2011

Ваш код заставляет браузер выполнять много повторяющихся, потраченных впустую работ. Подход, который вы должны использовать, заключается в следующем:

  1. Придумайте регулярное выражение, которое объединяет все ваши "целевые" регулярные выражения с другим, которое соответствует ведущей строке символов, которые не ваши специальные метасимволы.
  2. Измените цикл так, чтобы он делал следующее:
    1. Получить следующее совпадение из исходной строки. Это совпадение, из-за того, как вы изменили свое регулярное выражение, будет строкой немета-символов, за которыми следует ваша совпавшая часть.
    2. Добавить немета-символы и замену целевой части в отдельный массив строк.
  3. В конце этого процесса отдельный массив аккумуляторов может быть объединен и использован для замены содержимого.

Что касается того, как комбинировать регулярные выражения, ну, в JavaScript это не очень красиво, но выглядит так. Во-первых, вам нужно регулярное выражение для строки из нуля или более «неинтересных» символов. Это должно быть первой группой захвата в регулярном выражении. Далее должны быть альтернативы для целевых строк, которые вы ищете. Таким образом, общая форма:

var tokenizer = /(uninteresting pattern)?(?:(target 1)|(target 2)|(target 3)| ... )?/;

Когда вы сопоставите это с исходной строкой, вы получите массив результатов, который будет содержать следующее:

result[0] - entire chunk of string (not used)
result[1] - run of uninteresting characters
result[2] - either an instance of target type 1, or null
result[3] - either an instance of target type 2, or null
...

Таким образом, вы узнаете, какую цель замены вы видели, проверив, какие целевые регулярные выражения не пусты. (Обратите внимание, что в вашем случае цели могут предположительно перекрываться; если вы намерены, чтобы это сработало, то вам придется подходить к этому как к полной проблеме анализа, я подозреваю.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...