Why (โ๐โ.length === 2) in JavaScript
Today, we deep dive into a real world problem and debug the issue, find reason behind it and then we will discuss possible approach ofโฆ
Why (โ๐โ.length === 2) in JavaScript ?

Not a Member ? (Link here)
Today, we deep dive into a real world problem and debug the issue, find reason behind it and then we will discuss possible approach of solving that problem.
Letโs start. Here is a code , Can you guess what will be the output of it?
let arr = "A๐B";
console.log(arr.length); // Case 1 , ?
console.lof(arr.slice(0 , 2); // Case 2 , ?
You might think , In Case 1 , there are three characters so length of string arr should be 3 and In Case 2 , after applying slice operation on string arr , it should be โA๐โ.
Even , I thought the same.But when I run my compiler , the output that I get is different.
let arr = "A๐B";
console.log(arr.length); // 4
console.lof(arr.slice(0 , 2); // A๏ฟฝ
In Case 1 , The array of length is 4 and In Case 2 , the emoji get cut off which result in a question mark symbol.
Before discussing the reason behind it , letโs see how it will impact in real world development.
Suppose we are developing a login form for a social media platform where users are allowed to include emojis in their usernames to make them more appealing. The username length should be less than 10 characters. However, if a user includes 2โ3 emojis, the visual length of the username may still appear within the limit, but the actual character count can exceed. This will impact the user experience.
Another real world scenerio is let there is an input form for writing blogs in a blogging application where users can write anything. When displaying the list of all blogs on the dashboard, we show only the first 200 characters of each blog. However, if an emoji happens to be at the 200th index, it may get cut off, resulting in a question mark symbol being displayed. This negatively affects the user experience.
Now , Letโs get back to main issues , i.e. , javascript behaves different with string that contains characters outside the Basic Multilingual Plane(BMP).
In Javascript , Strings are stored internally as UTF-16 code units. In UTF-16 encoding , every code unit is 16 bits long means there are maximum of 65336 characters possible which includes BMP Characters. The entire unicode character set is much bigger , thus other extra characters are stored in UTF-16 as surrogate pairs , which are pairs of 1 code unit. It includes emojis, historical scripts, rare symbols, etc. To represent those, UTF-16 uses surrogate pairs:
- High surrogate (leading):
0xD800โ0xDBFF - Low surrogate (trailing):
0xDC00โ0xDFFF
Thus , we can say that
- BMP characters (AโZ, numbers, symbols, etc.) = 1 code unit.
- Supplementary characters (emojis, rare scripts, musical notes) = 2 code units.
Thatโs why, in Case 1, the length of the string (arr) is 4 due to the presence of an emoji character, because the length property of a string counts the number of code units, not the actual characters. Similarly, in Case 2, the slice method of a string also works based on code units.
Now that we understand the problem and the reason behind it , we can explore how to solve it. Before that , letโs learn about two methods charCodeAt and codePointAt.
What charCodeAt does
- Returns the UTF-16 code unit (0โ65535).
const str = "๐";
console.log(str.charCodeAt(0)); // 55357
console.log(str.charCodeAt(1)); // 56842
What codePointAt does
- Returns the full Unicode code point (can be larger than 65535).
console.log("๐".codePointAt(0)); // 128522
Some more practical example
const text = "Hi ๐";
for (let i = 0; i < text.length; i++) {
console.log(text.charCodeAt(i)); // breaks emoji into 2 numbers
}
for (let i = 0; i < text.length; i++) {
let codePoint = text.codePointAt(i);
console.log(codePoint, String.fromCodePoint(codePoint));
if (codePoint > 0xFFFF) i++; // skip surrogate
}
Solution โ
To properly handle Unicode characters:
- Use spread syntax (
[...str]) orArray.from(str)to iterate code points instead of code units.
const username = "Bob๐๐";
console.log([...username].length); // 5
- Works because
[...str]uses the iterator protocol โ iterates by code points (not UTF-16 code units). - Same for
Array.from(username).
- Iterating by Unicode code points
JavaScriptโs for...of loop uses [Symbol.iterator], which iterates correctly by code points:
for (const ch of "๐") {
console.log(ch); // "๐"
}
for...ofinternally uses[Symbol.iterator], so it also iterates by code points.- That means emojis characters arenโt split into surrogate halves.
- Use
codePointAt()+String.fromCodePoint()when iterating manually.
const text = "Bob๐๐";
for (let i = 0; i < text.length; i++) {
console.log(text.charCodeAt(i)); // breaks emoji into 2 numbers
}
for (let i = 0; i < text.length; i++) {
let codePoint = text.codePointAt(i);
console.log(codePoint, String.fromCodePoint(codePoint));
if (codePoint > 0xFFFF) i++; // skip surrogate
}
- Correct use of
codePointAt()โ gives you the full Unicode code point. - Correctly increments
i++again if the code point is > 0xFFFF (meaning it consumed 2 UTF-16 code units). - This is exactly how to manually traverse code points without breaking surrogate pairs.
Conclusion
JavaScript strings can be deceptive because they work at the UTF-16 code unit level, not at the level of human-perceived characters. Thatโs why "๐".length === 2 โ what looks like one character on screen is actually two code units under the hood.
This difference becomes critical in real-world apps: enforcing username length, slicing previews, or truncating blogs can all break if we donโt handle surrogate pairs correctly. A broken emoji or a miscounted character isnโt just a bug โ itโs a poor user experience.
In short, always think in terms of Unicode code points when user-facing text is involved. JavaScriptโs default string handling is code-unit-based (UTF-16), but with the right APIs (for...of, spread syntax, codePointAt, fromCodePoint) we can safely support emojis, rare scripts, and any Unicode character without breaking user experience.
Thanks for reading โ and as always,
Happy coding! ๐
A message from our Founder
Hey, Sunil here. I wanted to take a moment to thank you for reading until the end and for being a part of this community.
Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We donโt receive any funding, we do this to support the community. โค๏ธ
If you want to show some love, please take a moment to follow me on LinkedIn, TikTok, **Instagram. You can also subscribe to our [weekly newsletter](https://newsletter.plainenglish.io/)**.
And before you go, donโt forget to clap and follow the writer๏ธ!
๋ฉํ๋ฐ์ดํฐ
- post_id
- 4e8fad223bab
- slug
- why-length-2-in-javascript-4e8fad223bab
- url
- https://javascript.plainenglish.io/why-length-2-in-javascript-4e8fad223bab
- canonical_url
- https://javascript.plainenglish.io/why-length-2-in-javascript-4e8fad223bab
- author_url
- https://medium.com/@sneha12c
- status
- ok
- fetched_at
- 2026-07-17 02:49:50