concat()
ES3+Joins one or more strings onto the calling string and returns the combined result as a new string, leaving every input unchanged. Arguments that are not strings are converted to strings first. In modern code it is largely superseded by template literals and the + operator, which do the same job more readably.
Syntax
string.concat(str1, str2, ...)Parameters
strings string Strings to concatenate
Return Value
A new string containing the combined text
Examples
const str = 'Hello';
console.log(str.concat(' ', 'World')); 📌 When to Use
In practice, reach for concat() rarely and deliberately. For everyday joining, template literals are the modern default, since they interpolate variables inline, handle multi-line text, and make the intended output visible at a glance, while the + operator remains fine for trivial two-piece joins. concat() earns its place in a few niches. Because it is a method, it works as a first-class function reference in functional pipelines, for example as a reducer when folding an array of fragments, although join() on the array is almost always the better tool for exactly that job. It also accepts multiple arguments in one call, appending them all in order. And the empty-string trick, calling concat on an empty string literal, is an occasionally seen idiom for coercing mixed values to a string. Be aware of a semantic difference from the + operator: + performs general addition and only concatenates when an operand is a string, so 1 + 2 + ' items' computes 3 first, whereas ''.concat(1, 2, ' items') produces '12 items'; concat() always treats every argument as a string. When building large strings incrementally, prefer collecting parts in an array and joining once at the end, which is clearer and avoids repeated intermediate strings.
⚠️ Common Mistakes
Expecting concat() to modify the string it is called on: strings are immutable, so str.concat(x) returns a new string and str is unchanged; the result must be assigned. The method name misleads people who know mutable string builders from other languages.
Assuming it behaves like the + operator on numbers: + adds numbers until a string appears, while concat() stringifies every argument, so the two can produce different results from identical inputs. Code that switches between the styles can silently change output.
Building strings in a loop with repeated concat() calls and re-assignment: each call allocates a fresh string, though engines mitigate this with rope structures. Collect pieces in an array and join() once, which is both idiomatic and predictable.
Choosing concat() for readability-critical code: 'Hello, '.concat(name, '!') hides the output shape, whereas a template literal shows it; most style guides and linters actively prefer template literals.
Passing a Symbol: like all string coercion, concat() throws a TypeError for Symbol arguments, an occasionally surprising failure when values flow in from generic code.
Forgetting that null and undefined stringify: concat() happily produces 'value: null' and 'value: undefined' from nullish arguments rather than throwing, so missing data becomes literal text in the output unless screened first.
✅ Best Practices
Default to template literals for interpolation and multi-part construction; they are the readable standard, and reserve concat() for cases where a method reference is genuinely convenient.
Use Array.prototype.join() to combine lists of fragments, which handles separators cleanly and avoids quadratic-feeling accumulation patterns.
When appending in a loop is unavoidable, accumulate with += , which engines optimize with rope representations, or better, push parts into an array and join once at the end.
Guard against nullish values before concatenation so 'undefined' never appears in user-visible text, using defaults or nullish coalescing at the point of use.
If you rely on the always-stringify semantics of concat() as opposed to the addition semantics of +, leave a comment, because readers will assume the two are interchangeable.
Keep style consistent within a codebase: mixing template literals, +, and concat() in nearby code makes diffs noisy and reviews harder for no benefit.
⚡ Performance Notes
Concatenation cost in modern engines is dominated by representation strategy, not by which spelling you use: V8 and its peers build rope-like cons strings for concatenation, deferring the copy, so +, +=, template literals, and concat() all end up in the same optimized machinery, with the operator forms often slightly ahead because they are easier for the JIT to inline. The old advice that concat() is faster than + is obsolete. What actually matters is the pattern: appending in a loop builds a rope that is flattened into contiguous memory when the string is later read, which is efficient, while joining an array of parts performs one allocation of the final size and is the most predictable for large assemblies. Avoid quadratic patterns like repeatedly prepending to a growing string, and avoid flattening giant ropes repeatedly by interleaving reads (such as length-dependent slicing) with continued appending.
🌍 Real World Example
Dynamic String Building
A comparison of string-building styles applied to the same greeting and message assembly. The template literal version interpolates the name directly and reads exactly like its output; the + operator version works but fragments the text; and the concat() version chains the pieces as method arguments, the least common style in modern codebases. The example's real lesson is the pecking order: prefer template literals for interpolation, tolerate + for short joins, and know that concat() exists mainly so you can read older code that uses it, plus the join() pattern for combining many parts collected in an array.
// Modern approach: Template literals (preferred)
const name = 'World';
const greeting = `Hello, ${name}!`;
console.log(greeting); // 'Hello, World!'
// Using concat for dynamic parts from array
const parts = ['Hello', ' ', 'World', '!'];
const message = ''.concat(...parts);
console.log(message); // 'Hello World!'
// Comparison of approaches for building strings
function buildQueryString(params) {
// Using concat (less preferred)
// let query = '';
// Object.entries(params).forEach(([key, value]) => {
// query = query.concat(query ? '&' : '?', key, '=', value);
// });
// Better: Using array and join
const pairs = Object.entries(params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`);
return pairs.length > 0 ? '?' + pairs.join('&') : '';
}
console.log(buildQueryString({ name: 'John', age: 30 }));
// '?name=John&age=30'
// When concat is appropriate: programmatic string assembly
function buildPath(...segments) {
return segments
.filter(s => s && s.length > 0)
.map(s => s.replace(/^\/+|\/+$/g, ''))
.reduce((path, segment) => path.concat('/', segment), '');
}
console.log(buildPath('api', 'v1', 'users')); // '/api/v1/users'