Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 08.12.2008, 16:30
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 10.07.2008
Сообщений: 3,873

Регулярное выражение в замыкании, проблема в 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.
Почему так происходит? Стоит избегать попадания регулярных выражений в замыкание?

Последний раз редактировалось Octane, 08.12.2008 в 17:44. Причина: Исправил регулярное выражение
Ответить с цитированием
  #2 (permalink)  
Старый 08.12.2008, 17:22
Новичок на форуме
Отправить личное сообщение для Kolyaj Посмотреть профиль Найти все сообщения от Kolyaj
 
Регистрация: 19.02.2008
Сообщений: 9,177

Сообщение от Octane
styleFloat
Это в ИЕ styleFloat, в остальных cssFloat.

Сообщение от Octane
/\-([a-z]){1}/g
Минус экранировать необязательно, {1} вообще непонятно зачем.

А по теме: что говорит для разных строк, для нескольких черточек? Тестировать лень
Ответить с цитированием
  #3 (permalink)  
Старый 08.12.2008, 17:37
Кандидат Javascript-наук
Отправить личное сообщение для vk65535 Посмотреть профиль Найти все сообщения от vk65535
 
Регистрация: 21.11.2008
Сообщений: 114

Проблема в том, что в объекте регэкспа хранится смещение до следующего глобального поиска.
Я эту проблему решил так:
RegExp.prototype.reset = function() {
	this.lastIndex = 0;
	return this;
};

и всякий раз, когда нужно работать с глобальным регэкспом:
...
expr.reset().test(prop) ? prop.replace( ...
...
Ответить с цитированием
  #4 (permalink)  
Старый 08.12.2008, 17:39
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 10.07.2008
Сообщений: 3,873

Сообщение от Kolyaj Посмотреть сообщение
Это в ИЕ styleFloat, в остальных cssFloat.
Да я знаю. Эта функция будет использоваться для преобразования имен CSS-свойств в IE, чтобы получать значения «currentStyle», т.к. в других браузерах в метод «getPropertyValue» нужно отправлять значения вида: «float», «border-bottom-color», «background-image» и т.д.

Сообщение от Kolyaj Посмотреть сообщение
Минус экранировать необязательно, {1} вообще непонятно зачем.
Спасибо, поправил
var expr = /-([a-z])/g;

глюк остался
Сообщение от Kolyaj Посмотреть сообщение
А по теме: что говорит для разных строк, для нескольких черточек? Тестировать лень
В смысле не только для «background-image»? Ведет себя так же с любым параметром.

vk65535, спасибо за решение.
Не встречал в литературе описания подобных вещей. И странно почему такое поведение только в IE и почему именно через раз срабатывает и только в замыкании? Хочу понять, баг это или так должно быть?

Последний раз редактировалось Octane, 08.12.2008 в 17:54.
Ответить с цитированием
  #5 (permalink)  
Старый 08.12.2008, 19:02
Кандидат Javascript-наук
Отправить личное сообщение для vk65535 Посмотреть профиль Найти все сообщения от vk65535
 
Регистрация: 21.11.2008
Сообщений: 114

Дело даже не в осле - если есть глобальный флаг, смещение до начала следующего поиска хранится в самом объекте регэкспа. А поскольку в замыкании он используется один и тот же, это смещение всякий раз остается таким, каким оно стало в предыдущем вызове функции.
Если прогнать небольшой тест:
(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;
}

осел создаст новый инстанс регэкспа, а хитрая лиса - использует имеющийся, видимо экономит на компиляции.

Последний раз редактировалось vk65535, 08.12.2008 в 19:07.
Ответить с цитированием
  #6 (permalink)  
Старый 08.12.2008, 19:24
...
Отправить личное сообщение для Zibba Посмотреть профиль Найти все сообщения от Zibba
 
Регистрация: 13.10.2008
Сообщений: 225

Встречал упоминания об этом в литературе. Подробнее можно почитать
тут: http://www.unix.com.ua/orelly/webpro...pt/ch10_03.htm
и тут: http://www.unix.com.ua/orelly/webpro...pt/ch10_02.htm (в описании метода match() )

Последний раз редактировалось Zibba, 08.12.2008 в 19:31.
Ответить с цитированием
  #7 (permalink)  
Старый 08.12.2008, 19:55
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 10.07.2008
Сообщений: 3,873

Получается, если каждый раз нужен независимый поиск, нужно либо создавать новый объект «RegExp» или сбрасывать «lastIndex».
Ответить с цитированием
  #8 (permalink)  
Старый 08.12.2008, 20:07
Кандидат Javascript-наук
Отправить личное сообщение для vk65535 Посмотреть профиль Найти все сообщения от vk65535
 
Регистрация: 21.11.2008
Сообщений: 114

Только для функций test и exec.
Странно, в http://www.unix.com.ua/orelly/webpro...pt/ch10_02.htm написано, что match создает аналогичную ситуацию, но тесты этого не подтвердили.

Последний раз редактировалось vk65535, 08.12.2008 в 20:12.
Ответить с цитированием
  #9 (permalink)  
Старый 08.12.2008, 20:43
...
Отправить личное сообщение для Zibba Посмотреть профиль Найти все сообщения от Zibba
 
Регистрация: 13.10.2008
Сообщений: 225

Нет вы наверное что то перепутали, у 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, поэтому Ваши тесты этого и не подтвердили.

Последний раз редактировалось Zibba, 08.12.2008 в 20:45.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
проблема с обработкой DIV nerik AJAX и COMET 2 22.08.2008 17:40
Проблема с маркером kostian02 Общие вопросы Javascript 0 23.06.2008 16:10
проблема с передачей русского текста в Prototype subaru Prototype & script.aculo.us 2 26.07.2007 16:56