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

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

Edit:

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

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

5 ответов


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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

по словам spec:

если элемент и undefined или null, пусть далее быть пустой строкой; иначе пусть далее быть ToString (элемент).


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

  • какова история странности, которая делает 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, изменение поведения конкатенации строк приведет только к недостаткам, но не к преимуществам. Более того, это будет ломать старые коды, так что это не будет обратно совместимо.

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

    Array.prototype.join кажется, самый простой. Будь то красивая или не может быть мнения.


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

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

раздел 11.6.1: оператор сложения ( + )

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

производства AdditiveExpression : AdditiveExpression + MultiplicativeExpression вычисляется следующим образом:

  1. пусть lref быть результатом оценки AdditiveExpression.
  2. пусть lval быть GetValue (lref).
  3. пусть форума RREF быть результатом оценки MultiplicativeExpression.
  4. давайте rval быть GetValue (форума RREF).
  5. пусть lprim будет ToPrimitive (lval).
  6. пусть rprim быть ToPrimitive(rval).
  7. Если Тип(lprim) является Строка или тип(rprim) является строкой, то
    a. Возвращает строку, которая является результат конкатенации ToString (lprim), а затем Метод toString(rprim)
  8. возвращает результат применения операции добавления к ToNumber (lprim) и ToNumber (rprim). См. Примечание ниже 11.6.3.

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

Раздел 9.1 ToPrimitive

абстрактная операция ToPrimitive принимает вход


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

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

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

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

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

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

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

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

это изменение ломает

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

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

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

"use strict";

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

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

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

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

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

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


отказ от ответственности: этот ответ частично основан на мнении.


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

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

то же самое происходит и с номерами:

4 + " = '4'

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