Регулярное выражение в замыкании, проблема в IE
Написал функцию, для преобразования CSS-свойст:
border-bottom-color → borderBottomColor background-image → backgroundImage z-index → zIndex и т.д. Вот сама функция: function change(prop) { var expr = /-([a-z])/g; return prop == 'float' ? 'styleFloat' : expr.test(prop) ? prop.replace(expr, function () { return arguments[1].toUpperCase(); }) : prop; } Используем: alert([change('background-image'), change('background-image')]); Результат: backgroundImage,backgroundImage Все работает нормально :) Начинаем извращаться над функцией, занесем инициализацию переменной в замыкание, чтобы каждый раз при вызове функции «change» не создавать новый объект «RegExp»: var change = function () { var expr = /-([a-z])/g; return function(prop) { return prop == 'float' ? 'styleFloat' : expr.test(prop) ? prop.replace(expr, function () { return arguments[1].toUpperCase(); }) : prop; }; }(); Вызываем с теме же параметрами и получаем результат: backgroundImage,background-image Второй раз преобразования не произошло. Это еще не конец фокуса. Вызовем функцию 3 раза: alert([change('background-image'), change('background-image'), change('background-image')]); Получим: backgroundImage,background-image,backgroundImage Вызовем 4 раза: alert([change('background-image'), change('background-image'), change('background-image'), change('background-image')]); Получим: backgroundImage,background-image,backgroundImage,background-image и т.д. Такое поведение наблюдается только в IE любой версии, даже 8b2. Почему так происходит? Стоит избегать попадания регулярных выражений в замыкание? |
Цитата:
Цитата:
А по теме: что говорит для разных строк, для нескольких черточек? Тестировать лень :) |
Проблема в том, что в объекте регэкспа хранится смещение до следующего глобального поиска.
Я эту проблему решил так: RegExp.prototype.reset = function() { this.lastIndex = 0; return this; }; и всякий раз, когда нужно работать с глобальным регэкспом: ... expr.reset().test(prop) ? prop.replace( ... ... |
Цитата:
Цитата:
var expr = /-([a-z])/g; глюк остался Цитата:
vk65535, спасибо за решение. Не встречал в литературе описания подобных вещей. И странно почему такое поведение только в IE :) и почему именно через раз срабатывает и только в замыкании? Хочу понять, баг это или так должно быть? |
Дело даже не в осле - если есть глобальный флаг, смещение до начала следующего поиска хранится в самом объекте регэкспа. А поскольку в замыкании он используется один и тот же, это смещение всякий раз остается таким, каким оно стало в предыдущем вызове функции.
Если прогнать небольшой тест: (function() { var re = /\S+/g; return function(a) { var _ = re.exec(a); if (_) alert('"' + a + '" matches: "' + _[0] + '"'); else alert('"' + a + '" not matched'); return arguments.callee; } })()('aaa bbb ccc')('000 111 222')('--- +++ ===')('zzz xxx yyy'); То станет ясно, что поиск каждый раз продолжается с той позиции, где был завершен предыдущий. При этом, когда встречается конец строки, регэксп фэйлит совпадение, а следующее уже начинает опять с начала. Но различие между ослом и лисой все-таки есть: в случае function() { var re = /.../g; } осел создаст новый инстанс регэкспа, а хитрая лиса - использует имеющийся, видимо экономит на компиляции. |
Встречал упоминания об этом в литературе. Подробнее можно почитать
тут: http://www.unix.com.ua/orelly/webpro...pt/ch10_03.htm и тут: http://www.unix.com.ua/orelly/webpro...pt/ch10_02.htm (в описании метода match() ) |
Получается, если каждый раз нужен независимый поиск, нужно либо создавать новый объект «RegExp» или сбрасывать «lastIndex».
|
Только для функций test и exec.
Странно, в http://www.unix.com.ua/orelly/webpro...pt/ch10_02.htm написано, что match создает аналогичную ситуацию, но тесты этого не подтвердили. |
Нет вы наверное что то перепутали, у match() если она вызывается без глобального флага g, возвращаемый массив имеет помимо свойства length еще два свойства: index (а не lastIndex) содержащее номер позиции символа внутри строки, с которого начинается соответствие, и input, являющееся копией строки, в которой выполняется поиск.
То есть для регулярного выражения r, в котором не установлен флаг g, вызов s.match(r) возвращает то же значение, что и r.exec(s) (в отличие от match(), exec() возвращает массив, структура которого не зависит от наличия в регулярном выражении флага g). Когда же exec() вызывается для регулярного выражения с флагом g, метод устанавливает свойство lastIndex объекта регулярного выражения равным номеру позиции символа, следующего непосредственно за найденной подстрокой. Когда метод exec() вызывается для того же регулярного выражения второй раз, то он начинает поиск с символа, позиции которого хранится в lastIndex. Т.е. выходит что если мы завершаем поиск до того, как нашли последнее соответствие в одной строке и начинаем поиск в другой строке с тем же объектом RegExp, то нужно скинуть свойство lastIndex в 0. P.S. Методы search(), replace() и match() не задействуют свойство lastIndex, поэтому Ваши тесты этого и не подтвердили. |
Часовой пояс GMT +3, время: 21:26. |