How Is a JavaScript String Not an Object

How is a Javascript string not an object?

"Everything is an object"... that's one of the big misconceptions that exist all around the language.

Not everything is an object, there are what we call primitive values, which are string, number, boolean, null, and undefined.

That's true, a string is a primitive value, but you can access all the methods inherited from String.prototype as if it were an object.

The property accessor operators (the dot and the bracket notation), temporarily convert the string value to a String object, for being able to access those methods, e.g.:

"ab".charAt(1); // "b"

What happens behind the scenes is something like this:

new String("ab").charAt(1); // "b", temporal conversion ToObject

As with the other primitive values, such as Boolean, and Number, there are object wrappers, which are simply objects that contain the primitive value, as in your example:

var strObj = new String("");
strObj.prop = "foo";

typeof strObj; // "object"
typeof strObj.prop; // "string"

While with a primitive:

var strValue = "";
strValue.prop = "foo";

typeof strValue; // "string"
typeof strValue.prop; // "undefined"

And this happens because again, the property accessor on the second line above, creates a new temporal object, as:

var strValue = "";
new String(strValue).prop = "foo"; // a new object which is discarded
//...

Are strings objects?

Speaking about language types, Strings are values of the String type.

The language has five primitive types, which are String, Number, Boolean, Null and Undefined.

There are String objects (also for Number or Boolean), they are called primitive wrappers, they are created when you use the constructor function associated with them, for example:

typeof new String('foo'); // "object"
typeof 'foo'; // "string"

But don't get confused with them, you will rarely need to use primitive wrappers, because even if primitive values are not objects, you can still access their inherited properties, for example, on a string, you can access all members of String.prototype, e.g.:

'foo'.indexOf('o'); // 2

That's because the property accessor (the dot in this case) temporarily converts the primitive value to an object, for being able to resolve the indexOf property up in the prototype chain.

About the constructor function you have in your question, as you know, it won't return the string.

Functions called with the new operator return an implicit value, which is a new object that inherits from that function's prototype, for example:

function Test () {
// don't return anything (equivalent to returning undefined)
}

new Test() instanceof Test; // true, an object

If an object is returned from the constructor, that newly created object (this within the constructor) will be lost, so the explicit returned object will come out the function:

function Test2() {
return {foo: 'bar'};
}

new Test2().foo; // 'bar'

But in the case of primitive values, they are just ignored, and the new object from the constructor is implicitly returned (for more details check the [[Construct]] internal operation, (see step 9 and 10)).

Strings are not object then why do they have properties?

A string like "Hello" is not an object in JavaScript, but when used in an expression like

"Hello".indexOf(2)

A new object derived from the constructor function String is produced wrapping the string "Hello". And indexOf is a property of String.prototype so things work as expected, even though there is a lot of magic going on.

In the following case

> var s = "xyz"; s.prop = 1; console.log(s.prop);
undefined

The reason you see undefined is that:

  1. The variable s is given a value which is a primitive string
  2. In s.prop = 1 and property named prop is assigned to a new, anonymous wrapper object.
  3. In the third statment above, another new object is created to wrap the primitive s. That is not the same wrapper object as in the second statement, and it does not have a prop property, so undefined is produced when asking for its value according to the basic JavaScript rules.

How can a object both be a string and not be a string?

instanceof attempts to find the constructor (the right hand operand) prototype (as in the prototype property) in the object (the left hand operand) prototype chain. If the left hand operator is not an object then it wasn't really instanced by any constructor, so any left hand operator that is not an object would return false.

Now, if var (which isn't a valid identifier) is truly a primitive string then doing var.constructor.name would use an intermediate value that is a String object (usually referred as autoboxing). That would be an instance of String.

However there's no "autoboxing" when using instanceof. Therefore never an object. Therefore not an instance of any constructor.

How to check if x is an Object but not a String Object

Use instance of

return (typeof x === "object" && !(x instanceof String) && x !== null)

const isNotNullObject = function(x) {  return (typeof x === "object" && !(x instanceof String) && x !== null);};
console.log( isNotNullObject(String(5)), isNotNullObject(new String(5)))

check if an object is string in Javascript

string values are not String objects (which is why the instanceof fails)2.

To cover both cases using "type-checking" it would be typeof x === "string" || x instanceof String; the first only matches strings and the latter matches Strings.

The tutorial assumes that [only] String objects - or string values which are promoted1 - have a charAt method and so uses "duck-typing". If the method does exist, then it is called. If charAt is used out-of-bounds then an empty string "", which is a false-y value, is returned.

The tutorial code would also accept a string of "\0", while s && s.length would not - but it would also "work" on arrays (or jQuery objects, etc). Personally, I trust the caller to provide the allowed values/types and use as little "type-checking" or special-casing as possible.


1 For primitive values of string, number, and boolean there is a corresponding object type of String, Number, and Boolean, respectively. When x.property is used on one of these primitive values the effect is ToObject(x).property - hence the "promotion". This is discussed in ES5: 9.9 - ToObject.

Neither the null or undefined values have corresponding objects (or methods). Functions are already objects but have a historically different, and useful, typeof result.

2 See ES5: 8 - Types for the different types of values. The String Type, eg., represents a string value.

Difference between the javascript String Type and String Object?

Strings are a value type in JS, so they can't have any properties attached to them, no prototype, etc. Any attempt to access a property on them is technically performing the JS [[ToObject]] conversion (in essence new String).

Easy way of distinguishing the difference is (in a browser)

a = "foo"
a.b = "bar"
alert("a.b = " + a.b); //Undefined

A = new String("foo");
A.b = "bar";
alert("A.b = " + A.b); // bar

Additionally while

"foo" == new String("foo")

is true, it is only true due to the implicit type conversions of the == operator

"foo" === new String("foo")

will fail.



Related Topics



Leave a reply



Submit