JavaScript String concatenation behavior with null or undefined values
Solution 1
What is the prettiest way to concatenate String with potential null or undefined object without falling into this problem [...]?
There are several ways, and you partly mentioned them yourself. To make it short, the only clean way I can think of is a function:
const Strings = {};
Strings.orEmpty = function( entity ) {
return entity || "";
};
// usage
const message = "This is a " + Strings.orEmpty( test );
Of course, you can (and should) change the actual implementation to suit your needs. And this is already why I think this method is superior: it introduced encapsulation.
Really, you only have to ask what the "prettiest" way is, if you don't have encapsulation. You ask yourself this question because you already know that you are going to get yourself into a place where you cannot change the implementation anymore, so you want it to be perfect right away. But that's the thing: requirements, views and even envrionments change. They evolve. So why not allow yourself to change the implementation with as little as adapting one line and perhaps one or two tests?
You could call this cheating, because it doesn't really answer how to implement the actual logic. But that's my point: it doesn't matter. Well, maybe a little. But really, there is no need to worry because of how simple it would be to change. And since it's not inlined, it also looks a lot prettier – whether or not you implement it this way or in a more sophisticated way.
If, throughout your code, you keep repeating the ||
inline, you run into two problems:
- You duplicate code.
- And because you duplicate code, you make it hard to maintain and change in the future.
And these are two points commonly known to be anti-patterns when it comes to high-quality software development.
Some people will say that this is too much overhead; they will talk about performance. It's non-sense. For one, this barely adds overhead. If this is what you are worried about, you chose the wrong language. Even jQuery uses functions. People need to get over micro-optimization.
The other thing is: you can use a code "compiler" = minifier. Good tools in this area will try to detect which statements to inline during the compilation step. This way, you keep your code clean and maintainable and can still get that last drop of performance if you still believe in it or really do have an environment where this matters.
Lastly, have some faith in browsers. They will optimize code and they do a pretty darn good job at it these days.
Solution 2
You can use Array.prototype.join
to ignore undefined
and null
:
['a', 'b', void 0, null, 6].join(''); // 'ab6'
According to the spec:
If element is
undefined
ornull
, Let next be the empty String; otherwise, let next be ToString(element).
Given that,
-
What is the history behind the oddity that makes JS converting
null
orundefined
to their string value inString
concatenation?In fact, in some cases, the current behavior makes sense.
function showSum(a,b) { alert(a + ' + ' + b + ' = ' + (+a + +b)); }
For example, if the function above is called without arguments,
undefined + undefined = NaN
is probably better than+ = NaN
.In general, I think that if you want to insert some variables in a string, displaying
undefined
ornull
makes sense. Probably, Eich thought that too.Of course, there are cases in which ignoring those would be better, such as when joining strings together. But for those cases you can use
Array.prototype.join
. -
Is there any chance for a change in this behavior in future ECMAScript versions?
Most likely not.
Since there already is
Array.prototype.join
, modifying the behavior of string concatenation would only cause disadvantages, but no advantages. Moreover, it would break old codes, so it wouldn't be backwards compatible. -
What is the prettiest way to concatenate String with potential
null
orundefined
?Array.prototype.join
seems to be the simplest one. Whether it's the prettiest or not may be opinion-based.
Solution 3
The ECMA Specification
Just to flesh out the reason it behaves this way in terms of the spec, this behavior has been present since version one. The definition there and in 5.1 are semantically equivalent, I'll show the 5.1 definitions.
Section 11.6.1: The Addition operator ( + )
The addition operator either performs string concatenation or numeric addition.
The production AdditiveExpression : AdditiveExpression + MultiplicativeExpression is evaluated as follows:
- Let lref be the result of evaluating AdditiveExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating MultiplicativeExpression.
- Let rval be GetValue(rref).
- Let lprim be ToPrimitive(lval).
- Let rprim be ToPrimitive(rval).
- If Type(lprim) is String or Type(rprim) is String, then
a. Return the String that is the result of concatenating ToString(lprim) followed by ToString(rprim)- Return the result of applying the addition operation to ToNumber(lprim) and ToNumber(rprim). See the Note below 11.6.3.
So, if either value ends up being a String
, then ToString
is used on both arguments (line 7) and those are concatenated (line 7a). ToPrimitive
returns all non-object values unchanged, so null
and undefined
are untouched:
The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. The abstract operation ToPrimitive converts its input argument to a non-Object type ... Conversion occurs according to Table 10:
For all non-Object
types, including both Null
and Undefined
, [t]he result equals the input argument (no conversion).
So ToPrimitive
does nothing here.
Finally, Section 9.8 ToString
The abstract operation ToString converts its argument to a value of type String according to Table 13:
Table 13 gives "undefined"
for the Undefined
type and "null"
for the Null
type.
Will it change? Is it even an "oddity"?
As others have pointed out, this is very unlikely to change as it would break backward compatibility (and bring no real benefit), even more so given that this behavior is the same since the 1997 version of the spec. I would also not really consider it an oddity.
If you were to change this behavior, would you change the definition of ToString
for null
and undefined
or would you special-case the addition operator for these values? ToString
is used many, many places throughout the spec and
"null"
seems like an uncontroversial choice for representing null
. Just to give a couple of examples, in Java "" + null
is the string "null"
and in Python str(None)
is the string "None"
.
Workaround
Others have given good workarounds, but I would add that I doubt you want to use entity || ""
as your strategy since it resolves true
to "true"
but false
to ""
. The array join in this answer has the more expected behavior, or you could change the implementation of this answer to check entity == null
(both null == null
and undefined == null
are true).
Solution 4
Use coalescing - syntax is ??
Examples, can test in your browser console:
"Hello " + (null ?? "")
"Hello " + (undefined ?? "")
Both will yield: 'Hello '
Solution 5
Is there any chance for a change in this behavior in future ECMAScript versions?
I would say the chances are very slim. And there are several reasons:
We already know what ES5 and ES6 look like
The future ES versions are already done or in draft. Neither one, afaik, changes this behavior. And the thing to keep in mind here is that it will take years for these standards to be established in browsers in the sense that you can write applications with these standards without relying on proxy tools that compile it to actual Javascript.
Just try to estimate the duration. Not even ES5 is fully supported by the majority of browsers out there and it will probably take another few years. ES6 is not even fully specified yet. Out of the blue, we are looking at at least another five years.
Browsers do their own things
Browsers are known to make their own decisions on certain topics. You don't know whether all browsers will fully support this feature in exactly the same way. Of course you would know once it is part of the standard, but as of now, even if it was announced to become part of ES7, it would only be speculation at best.
And browsers may make their own decision here especially because:
This change is breaking
One of the biggest things about standards is that they usually try to be backwards compatible. This is especially true for the web where the same code has to run on all kinds of envrionments.
If the standard introduces a new feature and it's not supported in old browsers, that's one thing. Tell your client to update their browser to use the site. But if you update your browser and suddenly half the internet breaks for you, that's a bug uhm-no.
Sure, this particular change is unlikely to break a lot of scripts. But that's usually a poor arguments because a standard is universal and has to take every chance into account. Just consider
"use strict";
as the instruction to switch to strict mode. It goes to show huw much effort a standard puts into trying to make everything compatible, because they could've made strict mode the default (and even only mode). But with this clever instruction, you allow old code to run without a change and still can take advantage of the new, stricter mode.
Another example for backwards compatibility: the ===
operator. ==
is fundamentally flawed (though some people disagree) and it could've just changed its meaning. Instead, ===
was introduced, allowing old code to still run without breaking; at the same time allowing new programs to be written using a more strict check.
And for a standard to break compatibility, there has to be a very good reason. Which brings us to
There is just no good reason
Yes, it bugs you. That's understandable. But ultimately, it is nothing that can't be solved very easily. Use ||
, write a function – whatever. You can make it work at almost no cost. So what is really the benefit for investing all the time and effort into analyzing this change which we know is breaking anyway? I just don't see the point.
Javascript has several weak points in its design. And it has increasingly become a bigger issue as the language became more and more important and powerful. But while there are very good reasons to change a lot of its design, other things just arent't meant to be changed.
Disclaimer: This answer is partly opinion-based.
Related videos on Youtube
Doc Davluz
Updated on October 02, 2021Comments
-
Doc Davluz over 2 years
As you may know, in JavaScript
'' + null = "null"
and'' + undefined = "undefined"
(in most browsers I can test: Firefox, Chrome and IE). I would like to know the origin of this oddity (what the heck was in the head on Brendan Eich?!) and if there is any aim for changing it in a future version of ECMA. It's indeed pretty frustrating having to do'sthg' + (var || '')
for concatenating Strings with variables and using a third party framework like Underscore or other for that is using a hammer for jelly nail pounding.Edit:
To meet the criteria required by StackOverflow and clarify my question, it is a threefold one:
- What is the history behind the oddity that makes JS converting
null
orundefined
to their string value inString
concatenation? - Is there any chance for a change in this behavior in future ECMAScript versions?
- What is the prettiest way to concatenate
String
with potentialnull
orundefined
object without falling into this problem (getting some"undefined"
of"null"
in the middle of the String)? By the subjective criteria prettiest, I mean: short, clean and effective. No need to say that'' + (obj ? obj : '')
is not really pretty…
-
Michele about 10 years+1 Agree, I was expecting the same behaviour (which now costs me lot of if condition that slows data my apps). I have an
R
background and in there you get what you saidpaste0("a", NULL) == "a"
-
Mike Lippert over 9 yearsActually the behavior is exactly what I would expect. null and undefined are converted to their string representations which are different from the empty string, but it would be easy enough to write a function that looked at its arg and returned the empty string if it were null or undefined, and the toString of anything else.
-
Doc Davluz over 9 years@MikeLipper Easy enough but not native and no coalescence with this (no operator overloading in JS, hopefully). Coalescence is something great in JS for compact code and this advantage is broken by this behavior. You have to use workarounds. And if I ask the question, it is that this behavior is far from what I expect.
-
jongo45 over 9 yearsIt's a nice way of letting you know that some value wasn't assigned an actual string value and/or lacks
toString
orvalueOf
methods. I would imagine there are as many circumstances where you would want them to show asnull
orundefined
as you would want them to show as an empty string. Fortunately, I hardly ever run into this problem since I often concatenate strings with join (it solves other issues such as doing concatenations while using ternary (?
) expressions).
- What is the history behind the oddity that makes JS converting
-
Doc Davluz over 10 yearsI know the coalescence in JS, but it isn't sufficient to justify this. A null or undefined object could coalesce in an empty String, I don't see a problem with that. Do you see one?
-
Keen over 9 yearsI agree that this is the best answer to the third part of the question. I wanted to upvote, but the question has two other parts. Then again, I imagine this should just be posted as three different questions.
-
Oriol over 9 years@Cory True, I have completed the answer.
-
Doc Davluz over 9 yearsYou're answer is perfect as it seems to be the more efficient and elegant way to deal with this problem. I accept it even if it doesn't answer to the two first points that are darn ones. @Oriol also deserved some merit because he also answers those missing points.
-
a better oliver over 9 yearsIt's ironic that you warn about duplicated code while presenting an approach that leads to duplicated code.
-
Ingo Bürk over 9 years@zeroflagL you are free to encapsulate even more on a use case abstraction level. But I don't know about use cases involved. I am presenting an idea.
-
Rob Gilliam about 9 yearsI think you mean "criterion" (from the Greek for a standard or judgement), rather than "criterium" (the Latin equivalent, but usually only used in English for a type of bicycle race, actually taken from the French "critérium" for "competition")
-
Madbreaks about 9 years"You can use Array.prototype.join to ignore undefined and null" - it's worth pointing out that this only works as desired if you're using an empty string as your
join
arg. -
Fyodor Yemelyanenko over 4 yearsAdd
.trim()
after.join()
and it will remove leading/trialing spaces. (In case of joining using space). Similarly other types of chars can be removed -
Yunnosch over 2 yearsI recommend against rhetoric questions in answers. They risk being misunderstood as not an answer at all. You are trying to answer the question at the top of this page, aren't you? Otherwise please delete this post.
-
Yunnosch over 2 yearsWhile this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
-
Don Cheadle over 2 years@Yunnosch I'm answering this question:
What is the prettiest way to concatenate String with potential null or undefined object without falling into this problem
. My answer does not contain a rhetorical question - ?? is literally the syntax... -
Yunnosch over 2 yearsPlease have a look at stackoverflow.com/editing-help it might help you to make the difference between (exaggerated) prose punctuation and code syntax more obvious.
-
Chris Marisic about 2 yearsUsing this in conjunction with string interpolation for url formulation:
let url = `${AREA_ROOT}/Projects/${[projectId].join('')}`