Подтвердить что ты не робот

Проблема с типом принуждения и конкатенацией строк в JavaScript в Greasemonkey script в Firefox

Я создаю GreaseMonkey script, чтобы улучшить пользовательский интерфейс 10k инструментов Stack Overflow. Я столкнулся с невоспроизводимой и откровенно странной проблемой, которая запутала меня и других в JavaScript-зале в SO Chat. Нам еще предстоит найти причину после нескольких длительных отладки сеансов.

Проблематичный script можно найти здесь. Источник - Установить


Проблема возникает в строке 85, строка после комментария "vodoo":

return (t + ' (' + +(+f.offensive + +f.spam) + ')');

Это может показаться немного странным, но + перед двумя переменными, а внутренняя скобка - для принудительного типа, внутренняя средняя + - для добавления, а другая для конкатенации.

Ничего особенного, но наблюдательный читатель может заметить, что принуждение типа на внутреннем скобке не является необходимым, поскольку оба типа уже привязаны к числам, а результат принудительного ввода бесполезен, когда они все равно конкатенируются в строку. Не так! Удаление + прерывает script, в результате чего f.offensive и f.spam объединяются, а не объединяются вместе.

Добавление далее console.log только делает вещи более запутанными:

console.log(f.offensive + f.spam); // 50
console.log('' + (+f.offensive + +f.spam)); // 5, but returning this yields 50 somehow
console.log('' + (+f.offensive + +f.spam) + ''); // 50

Источник: http://chat.stackoverflow.com/transcript/message/203261#203261


Проблема заключается в том, что это невоспроизводимые скрипты, такие как

console.log('a' + (+'3' + +'1') + 'b');

в консоли Firebug дает правильный результат, как и

(function(){
    return 'a' + (+'3' + +'1') + 'b';
})();

Даже вытащить большие куски кода и запустить их в консоли, не воспроизводит эту ошибку:

$('.post-menu a[id^=flag-post-]').each(function(){
    var f = {offensive: '4', spam: '1'};

    if(f){
        $(this).text(function(i, t){
            // Vodoo - please do not remove the '+' in front of the inner bracket
            return (t + ' (' + +(+f.offensive + +f.spam) + ')');
        });
    }
});

Тим Стоун в чате имеет инструкцию для воспроизведения для тех, кто ниже 10k.


Эта ошибка появляется только в Firefox. У Chrome не появляется эта проблема, поэтому мне кажется, что это может быть проблемой с движком Firefox JavaScript или надстройкой Greasemonkey. Я прав?

Я могу найти в JavaScript-комнате, если вы хотите более подробно и/или хотите обсудить это.

4b9b3361

Ответ 1

Как часть процесса пользовательского ввода, тег <script> вводится на страницу с кодом, полученным при вызове toString() в функции, которую вы определили. Обычно это будет хорошо, но похоже, что в движке javascript, используемом Firefox 3.6.13, есть ошибка, которая перемещает скобки в выражении, заставляя его оценивать по-разному, когда функция toString() -ified обрабатываются.

Чтобы проиллюстрировать эту проблему, мы можем запустить следующий код в Firebug:

function f() { var a = '', b = '1', c = '2'; return a + '(' + (+b + +c) + ')'; };
f.toString();

Это дает нам этот результат:

function f() {
    var a = "", b = "1", c = "2";
    return a + ("(" + + b + + c + ")");
}

Вы заметите, что возвращаемое выражение было изменено. Скобки были перемещены за пределы строк, которые ранее были вне их, заставляя переменные b и c принуждаться к строкам и объединяться. Это дает неожиданный результат, так как ожидаемое дополнение никогда не происходит. К сожалению, это поведение присутствует даже при использовании Number() или parseInt() для принуждения b и c.

Есть несколько небольших модификаций, которые меняют это значение, но самый чистый - просто сохранить результат добавления к переменной заранее:

$(this).text(function(i, t){
    var c = +f.offensive + +f.spam;
    return (t + ' (' + c + ')');
});

К счастью, эта проблема, похоже, не возникает в бета-версии Firefox 4, поэтому, надеюсь, эта проблема будет решена в будущем. Кроме того, Matthew Flaschen любезно пошел вперед, и подал отчет об ошибке (отмеченный дубликат 559438), чтобы разработчики были осведомлены об этой проблеме в любом случае.