Может ли кто-нибудь объяснить это странное поведение JS в отношении конкатенации строк?

Я только что разместил это в основе: https://gist.github.com/2228570

var out = '';

function doWhat(){
    out += '<li>';
    console.log(out === '<li>'); // at this point, out will equal '<li>'
    return '';
}

out += doWhat();
console.log(out, out === '<li>');
// I expect out to == '<li>', but it actually an empty string!?

Такое поведение странно, есть ли у кого-нибудь объяснения? Это сложная задача для Google. Это также не имеет значения, если вы используете out += или out = out +.

EDIT: @paislee создал JSFiddle, который демонстрирует, как, если doWhat находится на отдельной строке, он ведет себя так, как ожидалось: http://jsfiddle.net/paislee/Y4WE8/

Ответ 1

Кажется, вы ожидаете, что doWhat будет вызван до того, как будет оценен +=.

Но, прогрессия линии такова:

out += doWhat();      // original line
out = out + doWhat(); // expand `+=`
out = '' + doWhat();  // evaluate `out`, which is currently an empty string
out = '' + '';        // call `doWhat`, which returns another empty string
out = '';             // result

out += '<li>'; внутри doWhat обновляет переменную, но слишком поздно, чтобы иметь длительный эффект.

Ответ 2

Путаница в том, что вы ожидаете, что doWhat() также изменит out напрямую. По-видимому, значение, которое вы добавите, извлекается до вызова этой функции.

Здесь логика:

  • Получить значение out ('')
  • Вызовите doWhat() и добавьте результат к первому значению ('' + '' = '')
  • Назначьте результат out ('')

Смешивание возвращаемых значений и прямых изменений таким образом - это просто поиск проблем, как это точно продемонстрировано в вашем примере. Возможно, вам стоит попробовать вернуться <li>.

Ответ 3

Представьте, что это так:

// out is undefined
var out = '';
// out is ''
function doWhat(){
 out += '<li>';
 // out is '<li>';
 return '';
}
out = out + doWhat();
// out += doWhat(); is the same as:
// out = out + doWhat(); is the same as :
// out = '' + doWhat; because the value of `out` is read when `out` is first found
// out is ''

И линеаризован:

// out is undefined
var out = '';
// out is ''
out += '<li>';
// out is '<li>';
out = '' + ''; // first '' is value of `out` before function is called, second is what the function returns
// out is ''

Решение

var out = '';
function doWhat(){
 out += '<li>';
 return '';
}
out = doWhat() + out; // Note `out` is *after* `doWhat()`

TL; DR

В настоящее время ваш код оценивается так же, как:

out = out + doWhat();

Это считывает значение out до вызова doWhat(). Чтобы заставить его работать так, как вы ожидаете, просто измените порядок:

out = doWhat() + out;

Это будет читать значение out после doWhat(), как вы ожидаете.

Ответ 4

Как сказал Джонатан выше, вы возвращаете пустую строку из этой функции, поэтому, хотя вы изменяете глобальную переменную, добавляя '<li>' to out внутри doWhat, javascript собирается добавить возвращаемое значение от функции до значения out при вызове функции.

Вы также можете просто сделать:

var out = '';

function doWhat(){
    out += '<li>';
    return true;
}

doWhat();

Есть ли какая-то конкретная причина, по которой вам нужно добавить вещи в строку как внутри функции, так и после ее возврата?

[править]

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

var out = '', temp;

function doWhat(){
   out += '<li>';
}

out += typeof (temp = doWhat()) === 'undefined' ? '' : temp;

Из этой презентации значение temp всегда будет undefined, потому что функция не возвращает ничего, что можно ввести. Если вы планируете, что функция иногда возвращает значение, которое вы затем добавляете, вы можете добиться того, что ищете, разбив его на две строки в конце:

var out = '', temp;
function doWhat(){
    out += '<li>';
}
temp = doWhat();
out += typeof (temp === undefined) ? '' : temp;

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

var out = '';
function doWhat () {
   out += '<li>';
   if (some condition is met) { out += 'some extra string'; }
}
doWhat();