Строковый объект против буквального - изменение прототипа?

Мне интересно, почему кажется, что добавление метода к прототипу строкового литерала, похоже, работает, но добавление свойства не происходит? Я играл с идеями относительно этого вопроса и имел следующий код:

String.prototype._str_index1 = 0;
String.prototype._str_reset = function() {
    this._str_index1 = 0;
};
String.prototype._str_substr = function(len) {
  var ret = this.substr(this._str_index1, len);
  this._str_index1 = this._str_index1 + len;
  return ret;
};

var testString = new String('Loremipsumdolorsitamet,consectetur');
log(testString._str_substr(5));
log(testString._str_substr(4));
​

Это прекрасно работает. Если, однако, я изменяю третью последнюю строку на:

var testString = 'Loremipsumdolorsitamet,consectetur';

... кажется, что хотя метод _str_substr существует и может быть вызван в строковом литерале, значение свойства _str_index1 всегда равно 0.

Что?

Ответ 1

Строковый примитив преобразуется в объект переходного String каждый раз при попытке вызвать метод объекта String (механизм JavaScript внутренне преобразует примитив строки в объект String, когда это необходимо). После возвращения этой функции объект String (ненавязчиво) преобразуется обратно в примитив строки (под капотом), и этот новый примитив возвращается (и большую часть времени назначается переменной); при каждом вызове метода String.

Итак, после каждого вызова testString._str_substr, _str_index1 выбрасывается вместе с объектом, а новый объект (с reset _str_index1) создается, когда _str_substr вызывается снова.

См. также MDC:

Поскольку JavaScript автоматически преобразует примитивы строк и объекты String, вы можете вызвать любой из методов объекта String в примитиве строк. JavaScript автоматически преобразует примитив строки во временный объект String, вызывает метод, а затем отбрасывает временный объект String.

Ответ 2

Это происходит потому, что объект создается и сразу же отбрасывается, когда выполняется назначение, потому что это строковый литерал.

Итак, с первой версией объект создается и сохраняется, поэтому testString - это объект, а не строковый литерал. Во втором случае объект создается и удаляется, поэтому все свойства теряются...

Теперь попробуйте заменить эту строку следующим:

var testString = 'Loremipsumdolorsitamet,consectetur'._str_substr();

Интересно, верно? Он по-прежнему возвращает примитив строки, но это может быть исправлено...

String.prototype._str_substr = function(len) {
  var ret = this.substr(this._str_index1, len);
  this._str_index1 = this._str_index1 + len;
  return new String(ret);
};

Конечно, это просто предложения, призванные помочь объяснить, почему литералы действуют иначе, чем объекты, а не реальные рекомендации...