8

Everything in JS is an object. I've always known that, and I totally understand that. I know why {} !== {}. It's two different objects. Same as if you were to write out new Object() == new Object().

Some other examples:

{} == {} // => false
[] == [] // => false
/ / == / / // => false
new String() == new String() // => false

But, Strings are objects too (it's why you can do ''.replace() and extend them), so why does this work:

'' == '' // => true

Obviously it'd be a huge headache to compare two strings if this didn't work, but this seems inconsistent with the rest of the language. Internally, what's going on? Is it just a one-off or is there some other concept behind this?

Oscar Godson
  • 31,662
  • 41
  • 121
  • 201
  • 5
    Take a look at this: [Difference between the javascript String Type and String Object?](http://stackoverflow.com/questions/2051833/difference-between-the-javascript-string-type-and-string-object) – Blender May 21 '13 at 23:38
  • 1
    http://bclary.com/2004/11/07/#a-11.9.3 – moonshadow May 21 '13 at 23:40
  • "Everything in JS is an object" in the sense that even string primitives "inherits" from String prototype. But JS does have "primitivey" values which are not compared as objects. You can check this basic type with the `typeof` operator. And the spec for `===`: http://es5.github.io/#x11.9.6 – Fabrício Matté May 21 '13 at 23:44
  • @FabrícioMatté that is not true. String primitives absolutely do not "inherit" from the String prototype. They are a separate type, not objects. Same goes for boolean values and numbers. – Pointy May 21 '13 at 23:45
  • @Pointy what I meant is `''.toString === String.prototype.toString`, could you phrase it better? – Fabrício Matté May 21 '13 at 23:47
  • @Fabrício Matté: and it makes no sense. When you access a method - a temporary wrapper object is created. So you don't access a method of a string literal, but of a wrapper that is created for you implicitly. – zerkms May 21 '13 at 23:47
  • @zerkms Thanks for the clarification, but isn't it just an implementation detail? As it is done implicitly by the interpreter, it doesn't add much meaning besides that string primitives are immutable, or does it? – Fabrício Matté May 21 '13 at 23:52
  • @Fabrício Matté: this actually explains *why* they are immutable - because they are scalar. – zerkms May 21 '13 at 23:55
  • @FabrícioMatté yes - the fact is that a string literal really is not a String instance. The interpreter makes them seem similar sometimes, but they are not the same. – Pointy May 22 '13 at 04:28
  • @FabrícioMatté for example see the difference between `typeof "hello"` and `typeof new String("hello")` – Pointy May 22 '13 at 04:30
  • @Pointy Yes, I've commented exactly that on my first comment here. `:P` My last comment was asking about the implicit primitive value wrapping into objects that happens when methods are called, but I've figured it out on the accepted answer's comments, no worries. – Fabrício Matté May 22 '13 at 17:18

3 Answers3

4

JavaScript basically treats strings and numbers as scalars at all times, converting them to objects when a method is called, and converting back afterward, in cases where you aren't explicitly declaring new String("");

Same with numbers.

Without string/number/boolean equality, you'd have a hard time doing much of anything.

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • "and converting back afterward" nothing is converted back. Wrapper object is just thrown away. – zerkms May 21 '13 at 23:47
  • @zerkms Yes. You're correct. But in terms of the behind-the-curtain magic, nobody sees the wrapper in the first place. The string is a primitive until it needs to be an object, and then it's an object for the extent of its method calls, and then it's a primitive again. The actual implementation of the feature is quite different than the perceived behaviour (much like `++i` vs `i++`). – Norguard May 21 '13 at 23:50
  • anyway, I wouldn't say something is converted back, just because it's not correct ;-) – zerkms May 21 '13 at 23:51
  • @Norguard Yes, without it it'd be hard, but I was curious about the behind the scenes magic. Thank you! – Oscar Godson May 21 '13 at 23:52
  • @zerkms Just to be sure, instead of "converting", would you say that the primitive/scalar value (which is generated by the implicit String object when modified by a method) is returned then? – Fabrício Matté May 22 '13 at 00:05
  • I'm with zerkms. Primitives are converted to objects for the sake of evaluating an expression, it is the result of the expression that lives on, the original parts are history. – RobG May 22 '13 at 00:11
  • @FabrícioMatté a method called on a string will result in a new `String` object being created, passed the value of the primitive. The `String` then performs the method which was called, and returns the result. This is why if you were to say `var bob = "Bob"; bob.toUpperCase(); console.log(bob);`, you'd get `"Bob" and not "BOB" in the console. While the method returns the value, it doesn't modify the original variable (or point it at a different value). – Norguard May 22 '13 at 00:14
  • @RobG the original doesn't disappear, at all. The behind-the-scenes magic which zerkms is talking about is exactly why you need to save trimmed strings to a new variable (or back to the same variable). Because trimming or transforming the cases doesn't change the value referenced by the pointer (which is just used for the construction of the object, and not in the resolution of the function). As such, the original value lives on, as long as you maintain a reference to it, in memory. – Norguard May 22 '13 at 00:18
  • Nice crystal-clear explanations @Norguard. Though I already had knowledge of string primitives being immutable, your initial explanation makes the implicit String object wrapping more clear. My previous comment was questioning the return value of said methods - say `'Bob'.toUpperCase().toLowerCase()` - the primitive `"Bob"` is wrapped inside a String object, `toUpperCase` executes returning a (primitive?) string which is then wrapped again inside a String object to perform the `toLowerCase` operation, I'd assume. Well, guess it is not much use stressing over these implicit operations. – Fabrício Matté May 22 '13 at 00:23
  • @FabrícioMatté once you go that far, there's no telling what a given engine might do. If you wrote yourself a function which took a string and made the word proper-case, using a combination of chained methods, if that function was called multiple times, V8 might recompile that into a fundamentally different set of operations which produce the same output, but at a fraction of the cost (like not creating several intermittent String objects). But in terms of expected functionality, at a base level, for a JS engine to operate as-expected, yes. At minimum, you'd return a string, wrap it, etc... – Norguard May 22 '13 at 00:33
  • @Norguard makes perfect sense. Yeah I also assume V8 would recompile and optimize that. Thanks for the answers. `=]` – Fabrício Matté May 22 '13 at 00:35
  • @Norguard—you misunderstand my comment, perhaps it was badly worded. In your *bob* case, a new object is created solely for the sake of evaluating `bob.toUppserCase()`. That object (which I called the original as it's the original object made from the primitive) is not used again, it's history. It is not "converted back" or whatever. The original `bob` (whose value was always a primitive) remains unchanged because no new value was assigned. – RobG May 22 '13 at 03:32
  • @RobG I see what you mean, now. But to an end user, a person who knows JavaScript's behaviour, in and out, to the spec, but has absolutely no clue about __proto__ or about how method calls and context-passing and closure tables work, behind the curtain, but rather, just what you can do with them in the language, your comment doesn't make much sense, as you say "original", meaning an object which is never defined in the application, which does its job and returns before anybody even knows it's there. It's like __proto__ if you aren't looking for it, you won't see it. – Norguard May 22 '13 at 06:15
3

It is a one-off.

Reference

There is a difference between string literals and string objects. The article goes into more detail if you are interested.

The same is true for booleans and numbers. These primitives are compared different from objects.

km6zla
  • 4,787
  • 2
  • 29
  • 51
0

There are five primitive types in JavaScript: Number, String, Boolean, Undefined, and Null. Comparing the empty string literal "" to itself is no different from the comparison 5 === 5.

Alec Martin
  • 126
  • 2
  • 2
    No, it doesn't: http://f.cl.ly/items/3V3c0G3y0k1x2p1X0Q1Q/Screen%20Shot%202013-05-21%20at%204.54.47%20PM.png – Oscar Godson May 21 '13 at 23:54
  • Good catch. It seems I answered too quickly, before understanding the difference between a string literal and a string object. The answer has been edited to be correct. – Alec Martin May 22 '13 at 00:12