5

I had a similar condition required to check if the String type constructor name is "String" or "string".

I am lost with outputs of following JS code:

(typeof([].constructor.name)).constructor.name
"String"

typeof([].constructor.name).constructor.name
"string"

But when I test output of following code I get more confused:

(typeof([].constructor.name))
"string"

typeof([].constructor.name)
"string"

"string".constructor.name
"String"

According to my understanding the output should always be "String".

Can anyone put some bright light on what I am missing or whats going wrong with above code?

Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164

2 Answers2

1

A possible reason

After playing with the console I found the following, it seems like typeof(a).b might be equivalent to typeof (a).b so it seems like the following are equivalent

(typeof([].constructor.name)).constructor.name
(typeof [].constructor.name).constructor.name

However for the second example it seems like the following is executed

typeof([].constructor.name).constructor.name
typeof ([].constructor.name).constructor.name

The second parenthesis in the first example is acting as a grouping operator and that might be the reason of the weird results

Regarding the "string" and "String" values

As you may already know we can access the name of a named function through the name property

function A(){}
A.name // A

Also when you create a function behind the curtains an object is created which is accessible through the function's prototype property, this object has a reference to the function itself through its constructor property

function A(){}
A.prototype  // {constructor: A}
A.prototype.constructor === A // true

Whenever you create an "instance" of a function its hidden [[Prototype]] property (aka __proto__) points to the prototype of the constructor so

[].__proto__ === Array.prototype // true

Since an empty array doesn't have the property constructor defined on it, JS looks in the object pointed by __proto__ which for the empty array as seen above is the Array.prototype which has a constructor property therefore

[].constructor === Array // true

From this place the resolution of the operand gets trivial

[].constructor.name // "Array"
[].constructor.name.constructor === String // true
[].constructor.name.constructor.name // "String", i.e. the name of the `function String() {}`

So in your examples

(typeof [].constructor.name).constructor.name
// evaluated to
(typeof "Array").constructor.name
// evaluated to
"string".constructor.name
// evaluated to
String.name
// evaluated to
"String"

And for the second example

typeof ([].constructor.name).constructor.name
// evaluated to
typeof "String"
// evaluated to
"string"

Moral of the story: use typeof operator instead of typeof().

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Mauricio Poppe
  • 4,817
  • 1
  • 22
  • 30
  • "*it seems like `typeof(a).b` is equivalent to `typeof (a).b`*" - of course it is, that's how tokens and whitespaces work in JS. I guess what you actually wanted to emphasise is that `typeof a.b` is parsed as `typeof (a.b)` not `(typeof a).b` – Bergi May 27 '16 at 08:34
1

I had a similar condition required to check if the String type constructor name is "String" or "string".

The name of the String constructor is defined in the standard as "String" (ECMA-262 §4.3.19) with a capital "S", as is the convention for constructors in ECMAScript (though there are exceptions like Math and JSON).

The values returned by the typeof operator are also defined in the standard (ECMA-262 §12.5.6).

The Type of a value and the constructor that created it are two entirely separate concepts. E.g. any number of constructors with different names can return objects whose type is "object", but whose constructors have different (or the same) name.

The value returned by typeof does not necessarily match the Type of the value.

Just to go over some of the expressions:

(typeof([].constructor.name)).constructor.name

removing the unnecessary internal brackets and considering the outer brackets, it breaks down to:

[]           // returns an Array instance
.constructor // returns the Array constructor object
.name        // returns the name of the constructor, "Array"

so now there is:

(typeof 'Array') // returns the string primitive "string"

so now there is:

'string'       // string literal
.constructor   // returns the String constructor
.name          // returns the string "String"

so the final result is "String", the name of the String constructor.

And for the expression:

typeof([].constructor.name).constructor.name

Again starting with the brackets:

[]                  // returns an Array instance
.constructor        // returns the Array constructor
.name               // returns the string "Array"

so now there is:

'Array'             // the string 'Array'
.constructor        // the String constructor
.name               // the string 'String'

leaving:

typeof 'String'     // the string 'string'

So the final result is "string". The last is a consequence of the value returned by the typeof operator for primitive strings.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • What exception do you refer to in regard to `Math` and `JSON`? They're not constructors, and they don't have a `.name`. – Bergi May 27 '16 at 08:40
  • In that they are built–in objects whose name starts with a capital letter, but they aren't constructors (or even Functions). – RobG May 27 '16 at 08:41
  • Ah, that way round, sure. – Bergi May 27 '16 at 08:43