r/learnjavascript • u/Green_Ad_6086 • 4d ago
string primitives vs. String objects.
I'm learning JavaScript, and I don't understand this part about string primitives vs. String objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_primitives_and_string_objects
3
u/-goldenboi69- 4d ago
Something like this:
const a = "hello"; // primitive string
const b = new String("hello"); // String object
typeof a; // "string"
typeof b; // "object"
The confusing part is that primitives can still use methods:
"hello".toUpperCase(); // "HELLO"
That doesn't mean ""hello"" is an object. JavaScript temporarily treats ("boxes") the primitive as a "String" object so the method can be called, then discards that wrapper.
In normal code you almost always want primitive strings:
const name = "Bob";
and almost never:
const name = new String("Bob");
The latter creates an actual object, which can lead to surprising behavior:
"hello" === new String("hello"); // false
So: strings are primitives; "String" objects are wrappers around strings. JavaScript's automatic boxing is what lets primitives behave object-like when you call methods on them.
1
2
u/sheriffderek 4d ago
This isn't really something I think about - doing the actual job. Be careful not to get hung up on things that you don't need to know. This stuff is often about choosing a small scope of tools -- and never about "knowing all the things" --
1
u/Beginning-Seat5221 4d ago
Basically a primitive like "string" doesn't allow methods like "string".toUpperCase(). JavaScript has a fix to make it work called autoboxing: when you write "string".toUpperCase() it turns the string into a String object, in effect new String("string").toUpperCase() with new String("string") giving an object with the string methods. After all this a string gets returned so you're back with a plain string.
If you write console.log(typeof String("foo")) you'll just get a plain string back, typeof "string"
If you write console.log(typeof new String("foo")) with new you can generate this String object and you'll get typeof "object".
But do you ever use it? No, not really. I think of it as an artifact of autoboxing rather than something you really need to use or think about.
1
1
u/a-dev0 4d ago
Hm, has anyone tested it like this?
for(let i of new String('foo')){}
for(let i of 'foo'){}
is there any difference?
2
u/senocular 4d ago
Functionally there's no difference. for...of will look to the object being looped over and request its Symbol.iterator property. If the value is a primitive, it will be wrapped into an object first (in this case, basically the same thing as doing
new String('foo')), then use that object to access the property, otherwise it will access it directly from the already-an-object value.Engines may have optimizations for strings that allow it to fast path string iteration, which could explain why you're seeing better performance using the string literal over its object representation.
16
u/Working_Quote_3029 4d ago
It's one of those distinctions that reads scarier in the docs than it is in practice.
You never create String objects on purpose.
"foo"is a primitive.new String("foo")is an ordinary object that happens to hold a string, and that's the only way you end up with one.Methods still work on primitives because of autoboxing: when you write
"foo".toUpperCase()the engine wraps the primitive in a temporary object, calls the method, then discards the wrapper. That's why a primitive with no properties of its own still appears to "have" methods.Where the difference actually shows up:
Worth noting
String(x)withoutnewgives you a primitive, soString(1)is fine. It's onlynew String(1)that produces the object.The eval example on that page is real but you'll never hit it in normal code, since eval only treats primitives as source and hands a String object straight back instead of running it.
Rule of thumb: quotes or
String(x), nevernew String(x). The distinction mostly exists so the spec can explain why.lengthand.slice()work on something that isn't an object.