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

Поведение конкатенации строки JavaScript с нулевыми или undefined значениями

Как вы знаете, в JavaScript '' + null = "null" и '' + undefined = "undefined" (в большинстве браузеров я могу проверить: Firefox, Chrome и IE). Я хотел бы узнать происхождение этой странности (что, черт возьми, было в голове у Брендана Эйха?!), и если есть какая-то цель изменить его в будущей версии ECMA. Это действительно очень неприятно для того, чтобы сделать 'sthg' + (var || '') для конкатенации строк с переменными и использования сторонней структуры, такой как Underscore или другой, для этого используется молот для стука ногтей.

Edit:

Чтобы соответствовать критериям, требуемым StackOverflow, и уточнить мой вопрос, это тройной:

  • Какова история за странность, которая превращает JS-преобразование null или undefined в их строковое значение в конкатенации String?
  • Есть ли шанс для изменения этого поведения в будущих версиях ECMAScript?
  • Каким будет самый лучший способ объединить String с потенциальным объектом null или undefined, не попадая в эту проблему (получение некоторой "undefined" из "null" в середине строки)? По самым субъективным критериям я имею в виду: короткие, чистые и эффективные. Не нужно говорить, что '' + (obj ? obj : '') на самом деле не очень...
4b9b3361

Ответ 1

Каким будет самый лучший способ конкатенации String с потенциальным нулевым или undefined объектом, не попадая в эту проблему [...]?

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

var Strings = {};
Strings.orEmpty = function( entity ) {
    return entity || "";
};

// usage
var message = "This is a " + Strings.orEmpty( test );

Конечно, вы можете (и должны) изменить реальную реализацию в соответствии с вашими потребностями. И вот почему я думаю, что этот метод превосходит: он ввел инкапсуляцию.

Действительно, вам нужно только спросить, что такое "самый красивый" способ, если у вас нет инкапсуляции. Вы задаете себе этот вопрос, потому что вы уже знаете, что вы собираетесь попасть в место, где вы больше не можете изменить реализацию, поэтому вы хотите, чтобы он был совершенным сразу. Но вот что: требования, взгляды и даже перемены меняются. Они развиваются. Итак, почему бы не позволить себе изменить реализацию с помощью всего лишь одной линии и, возможно, одного или двух тестов?

Вы можете назвать это обманом, потому что на самом деле он не отвечает, как реализовать реальную логику. Но это моя точка: это не имеет значения. Ну, может быть, немного. Но на самом деле, нет необходимости беспокоиться из-за того, насколько просто было бы измениться. И поскольку он не встроен, он также выглядит намного красивее - независимо от того, реализуете ли вы этот способ или более сложным способом.

Если на протяжении всего кода вы повторяете строку ||, вы сталкиваетесь с двумя проблемами:

  • Вы дублируете код.
  • И поскольку вы дублируете код, вам трудно поддерживать и изменять в будущем.

И это два момента, которые обычно известны как анти-шаблоны, когда речь заходит о разработке высококачественного программного обеспечения.

Некоторые люди скажут, что это слишком много накладных расходов; они расскажут о производительности. Это не-смысл. Во-первых, это едва добавляет накладные расходы. Если это вас беспокоит, вы выбрали неправильный язык. Даже jQuery использует функции. Люди должны преодолеть микро-оптимизацию.

Другое дело: вы можете использовать код "compiler" = minifier. Хорошие инструменты в этой области попытаются определить, какие операторы встроены во время этапа компиляции. Таким образом, вы держите свой код в чистоте и обслуживании и все еще можете получить это последнее снижение производительности, если вы все еще верите в него или действительно имеете среду, в которой это имеет значение.

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

Ответ 2

Вы можете использовать Array.prototype.join для игнорирования undefined и null:

['a', 'b', void 0, null, 6].join(''); // 'ab6'

В соответствии с spec:

Если элемент undefined или null, пусть next - пустая строка; в противном случае пусть следующий будет ToString (element).


Учитывая, что

  •   
    Какова история за странность, которая превращает JS-преобразование null или undefined в их строковое значение в конкатенации String?
      

    Фактически, в некоторых случаях, текущее поведение имеет смысл.

      
    function showSum(a,b) {
        alert(a + ' + ' + b + ' = ' + (+a + +b));
    }
      

    Например, если функция выше вызывается без аргументов, undefined + undefined = NaN, вероятно, лучше, чем + = NaN.

      

    В общем, я думаю, что если вы хотите вставить некоторые переменные в строку, то отображение undefined или null имеет смысл. Вероятно, Эйх тоже подумал.

      

    Конечно, бывают случаи, когда игнорировать их было бы лучше, например, при объединении строк. Но для этих случаев вы можете использовать Array.prototype.join.

  •   
    Есть ли шанс для изменения этого поведения в будущих версиях ECMAScript?
      

    Скорее всего, нет.

      

    Поскольку уже существует Array.prototype.join, изменение поведения конкатенации строк может вызвать только недостатки, но никаких преимуществ. Более того, он разрушит старые коды, поэтому он не будет обратно совместим.

  •   
    Каков самый привлекательный способ конкатенации String с потенциалом null или undefined?
      

    Array.prototype.join кажется самым простым. Является ли это самым красивым или нет, может быть основанным на мнениях.

Ответ 3

Спецификация ECMA

Чтобы точно определить причину, по которой он ведет себя таким образом с точки зрения спецификации, это поведение присутствовало с версия one. Определение там и в 5.1 семантически эквивалентно, я покажу 5.1 определения.

Раздел 11.6.1: Оператор добавления (+)

Оператор сложения выполняет либо конкатенацию строк, либо числовую дополнение.

Производное AdditiveExpression: AdditiveExpression + Мультипликативное выражение оценивается следующим образом:

  • Пусть lref является результатом оценки AdditiveExpression.
  • Пусть lval - GetValue (lref).
  • Пусть rref является результатом оценки МультипликативноеВыражение.
  • Пусть rval - GetValue (rref).
  • Пусть lprim be ToPrimitive (lval).
  • Пусть rprim be ToPrimitive (rval).
  • Если тип (lprim) равен Строка или тип (rprim) - это строка, затем а. Вернуть строку, которая является результат конкатенации ToString (lprim), за которым следует ToString (rprim)
  • Вернуть результат применения операции добавления к ToNumber (lprim) и ToNumber (rprim). См. Примечание ниже 11.6.3.

Итак, если какое-либо значение заканчивается как String, тогда ToString используется для обоих аргументов (строка 7), и они объединяются (строка 7a). ToPrimitive возвращает все значения без объекта без изменений, поэтому null и undefined не затронуты:

Раздел 9.1 ToPrimitive

Абстрактная операция ToPrimitive принимает входной аргумент и необязательный аргумент PreferredType. Абстрактная операция ToPrimitive преобразует свой входной аргумент в тип не-объекта... Преобразование происходит в соответствии с таблицей 10:

Для всех типов не Object, включая null и undefined, [t]he result equals the input argument (no conversion). So ToPrimitive ничего не делает здесь.

Наконец, Раздел 9.8 ToString

Абстрактная операция ToString преобразует свой аргумент в значение типа String в соответствии с таблицей 13:

В таблице 13 приведены "undefined" для типа undefined и "null" для типа null.

Будет ли это изменено? Это даже "странность"?

Как отмечали другие, это вряд ли изменится, так как это приведет к нарушению обратной совместимости (и не принесет никакой реальной выгоды), тем более учитывая, что такое поведение остается неизменным с версии спецификации 1997 года. Я бы тоже не считал это странностью.

Если бы вы изменили это поведение, измените ли вы определение ToString для null и undefined или вы будете использовать специальный оператор для этих значений? ToString используется много, много мест по всей спецификации и "null" кажется бесспорным выбором для представления null. Чтобы привести несколько примеров, в Java "" + null находится строка "null", а в Python str(None) - строка "None".

Обход

Другие дали хорошие обходные пути, но я бы добавил, что я сомневаюсь, что вы хотите использовать entity || "" как свою стратегию, поскольку она разрешает true до "true", но false до "". Массив, соединяющий этот ответ, имеет более ожидаемое поведение, или вы можете изменить реализацию этого ответа на проверьте entity == null (как null == null, так и undefined == null).

Ответ 4

Есть ли вероятность изменения этого поведения в будущих версиях ECMAScript?

Я бы сказал, что шансы очень тонкие. И есть несколько причин:

Мы уже знаем, как выглядят ES5 и ES6

Будущие версии ES уже сделаны или в проекте. Ни один, афайк не меняет такого поведения. И здесь следует иметь в виду, что эти стандарты потребуются годы, чтобы эти стандарты были установлены в браузерах в том смысле, что вы можете писать приложения с этими стандартами, не полагаясь на прокси-инструменты, которые компилируют его на фактический Javascript.

Просто попробуйте оценить продолжительность. Даже ES5 полностью поддерживается большинством браузеров, и это, вероятно, займет еще несколько лет. ES6 еще не полностью определен. Мы смотрим на не менее пяти лет.

Браузеры делают свои собственные вещи

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

И браузеры могут принять собственное решение здесь, особенно потому, что:

Это изменение прерывается

Одна из самых больших проблем в отношении стандартов заключается в том, что они обычно стараются быть обратно совместимыми. Это особенно верно для Интернета, где один и тот же код должен работать на всех видах envrionments.

Если стандарт вводит новую функцию и не поддерживается в старых браузерах, это одна вещь. Сообщите своему клиенту обновить свой браузер, чтобы использовать сайт. Но если вы обновите свой браузер, и вдруг половина интернет-пауз для вас, это ошибка uhm-no.

Конечно, это конкретное изменение вряд ли сломает много скриптов. Но это, как правило, плохие аргументы, поскольку стандарт универсален и должен учитывать каждый случай. Просто рассмотрите

"use strict";

в качестве инструкции для перехода в строгий режим. Это показывает, что многие усилия прилагают усилия, чтобы сделать все совместимым, потому что они могли бы сделать строгий режим по умолчанию (и даже только для режима). Но с этой умной инструкцией вы разрешаете запуск старого кода без изменений и все еще можете использовать новый более строгий режим.

Другой пример обратной совместимости: оператор ===. == является фундаментальным недостатком (хотя некоторые люди не согласны), и он мог просто изменить его значение. Вместо этого был введен ===, позволяющий старому коду продолжать работать без нарушения; в то же время позволяя писать новые программы с использованием более строгой проверки.

И для стандартного нарушения совместимости должна быть очень веская причина. Это подводит нас к

Нет веской причины

Да, это вас задевает. Это понятно. Но, в конечном счете, это ничего не может решить очень легко. Используйте ||, напишите функцию - что угодно. Вы можете заставить его работать практически без затрат. Так что же действительно выгодно вкладывать все время и силы в анализ этого изменения, которое, как мы знаем, все равно ломается? Я просто не вижу смысла.

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


Отказ от ответственности: Этот ответ частично основан на мнениях.

Ответ 5

Чтобы добавить null и '', они должны соответствовать критерию минимального общего типа, который в этом случае является строковым типом.

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

То же самое происходит с числами:

4 + '' = '4'

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