38

This one just stabbed me hard. I don't know if it's the case with all browsers (I don't have any other competent browser to test with), but at least Firefox has two kind of string objects.

Open up the Firebugs console and try the following:

>>> "a"
"a"
>>> new String("a")
String { 0="a"}

As you can visually observe, Firefox treats new String("a") and "a" differently. Otherwise however, both kinds of strings seem to behave the same. There is, for instance, evidence that both use the same prototype object:

>>> String.prototype.log = function() { console.log("Logged string: " + this); }
function()
>>> "hello world".log()
Logged string: hello world
>>> new String("hello world").log()
Logged string: hello world

So apparently, both are the same. That is, until you ask for the type.

>>> typeof("a")
"string"
>>> typeof(new String("a"))
"object"

We can also notice that when this is a string, it's always the object form:

>>> var identity = function() { return this }
>>> identity.call("a")
String { 0="a"}
>>> identity.call(new String("a"))
String { 0="a"}

Going a bit further, we can see that the non-object string representation doesn't support any additional properties, but the object string does:

>>> var a = "a"
>>> var b = new String("b")
>>> a.bar = 4
4
>>> b.bar = 4
4
>>> a.bar
undefined
>>> b.bar
4

Also, fun fact! You can turn a string object into a non-object string by using the toString() function:

>>> new String("foo").toString()
"foo"

Never thought it could be useful to call String.toString()! Anyways.

So all these experiments beg the question: why are there two kinds of strings in JavaScript?


Comments show this is also the case for every primitive JavaScript type (numbers and bools included).

zneak
  • 134,922
  • 42
  • 253
  • 328

3 Answers3

14

There are two types of strings in Javascript -- literal strings and String objects. They do behave a little differently. The main difference between the two is that you can add additional methods and properties to a String object. For instance:

var strObj = new String("object mode");
strObj.string_mode = "object"
strObj.get_string_mode = function() { return this.string_mode; }

// this converts it from an Object to a primitive string:
str = strObj.toString();

A string literal is just temporarily cast to a String object to perform any of the core methods.

The same kinds of concepts apply to other data types, too. Here's more on primitive data types and objects.

EDIT

As noted in the comments, string literals are not primitive strings, rather a "literal constant whose type is a built-in primitive [string] value", citing this source.

Kelly
  • 40,173
  • 4
  • 42
  • 51
  • 4
    It's string *primitives* rather than literals. See my comment to Raynos's answer. – Tim Down Apr 01 '11 at 14:26
  • The terms _literal_ and _primitive_ seem to be used loosely, whether or not that is by-the-book. Wikipedia uses both terms in the same thought: "JavaScript provides a Boolean data type with true and false literals. The typeof operator returns the string 'boolean' for these primitives." – Kelly Apr 01 '11 at 14:41
  • @Kelly: They're different things though. In the case of string, a literal produces a primitive string, but string primitives are also produced by calling `toString()` on any native JavaScript object. – Tim Down Apr 01 '11 at 14:44
  • @TimDown it's string _values_ rather then primitives. See ES5 specification. – Raynos Apr 01 '11 at 14:48
  • @Raynos: *primitive* is very much an acceptable term, and is applied to the collective of string, number and boolean values throughout the ECMAScript 5 specification. There's also the `ToPrimitive` internal method and `[[PrimitiveValue]]` attribute, the former returns a primitive value which could be any of the primitive types. So either string value or string primitive should be an acceptable term. – Andy E Apr 01 '11 at 15:15
  • @AndyE that's the primitive value of an object. That's subtly different. But it is a acceptable term. – Raynos Apr 01 '11 at 15:16
  • @Raynos: You're right, even the third edition of the spec does refer to them as string values. – Tim Down Apr 04 '11 at 14:17
2

Your comparing string values to string objects.

"a" is a string value.

"a" === "a"; // true

new String("a") is an string object.

new String("a") === new String("a"); // false

There both strings. "a" just gets the string value "a" where as new String("a") creates a new string object which internally has the string value "a"

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • Does that mean `new String("foo" + "bar").toString()` creates a global, static `"foobar"` string? – zneak Apr 01 '11 at 14:02
  • Create might be the wrong word. It returns a string literal though. – Raynos Apr 01 '11 at 14:14
  • How does it bring "foobar" into existence if "creating" is not the right verb? Also, do you think those "global static" strings ever get garbage-collected/recycled? – zneak Apr 01 '11 at 14:20
  • @zneak global static is the wrong word to use. I'm not sure where the literals live internally in the javascript engine. I presume they live in a symbol table somewhere. But calling `toString()` returns a literal which is made and assigned a place in RAM somewhere and if you ever call it again you get the same object back. – Raynos Apr 01 '11 at 14:23
  • 2
    Slightly inaccurate: the difference is between string primitives and String objects. String literals do produce string primitives but are not the only way to do so. `toString()` on any JavaScript object produces a string primitive too. – Tim Down Apr 01 '11 at 14:25
  • 2
    Agreed, I would fix up the terminology. A "literal" defines the grammar necessary to create something without using a constructor function. A literal can create a primitive or an object (see object/array literals) and as @Tim Down said, primitives can be created from objects. – Andy E Apr 01 '11 at 14:35
  • 1
    @TimDown @AndyE changed the wording to match ES5 specification. – Raynos Apr 01 '11 at 14:48
0

Another important thing to remember is the following:

typeof "my-string" // "string"
typeof String('my-string') // 'string'
typeof new String("my-string") // "object". 

Therefore, when testing whether an argument or variable is a string you need to do at least the following.

function isString(arg) {
  if (!arg) {
    return false;
  }
  return typeof arg == "string" || arg.constructor == String;
}

The above function will still fail if you pass in a String object from another frame/iframe. That is:

frames[0].myStringVar.constructor != frames[1].myStringVar.constructor

That is because the constructor String is different for each window context. Therefore, a foolproof isString method would be

function isString(obj) {
   return Object.prototype.toString.call(obj) == "[object String]";
}
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217