11

Stoyan Stefanov in his excellent book 'Object-Oriented JavaScript' says:

Any value that doesn't belong to one of the five primitive types listed above is an object.

With five primitive types he means Number, String, Boolean, Undefined and Null. However in Google Chrome console it seems like number is not primitive type at all (compared to C primitive types like int). It looks like the primitive number has methods:

var a = 2.2;
console.log(a.toFixed()); // logs "2"

Thus I assumed that I can work with number as with an object, so I tried to assign a property to it:

var a = 2;
a.foo = 'bar';
console.log(a.foo); // logs undefined

I don't understand that behavior. If number has a method, it should behave like object, shouldn't it? It even has a prototype:

Number.prototype.foo = 'bar';
var a = 2;
console.log(a.foo); // logs 'bar'

So what is the magic behind this? How JavaScript treats objects versus primitive types? I would rather not use the word primitive and substitute it with simple objects. As I see it those are objects which can't be extended with new properties, however they are constructed through their constructor and also have prototype which can be extended like with normal objects.

Roman Mazur
  • 512
  • 6
  • 17
  • 1
    It's only undefined because you didn't create your own custom 'foo' method on it. When you did ( number.prototype.), it's fine. This is not'magic' just class behaviour. ( available to all 'types') So I'm not sure how to can answer your question "what is the magic behind this?" :L ) – Rob Sedgwick Feb 21 '14 at 11:32
  • If theres anything left unclear, just put a comment or edit the question, i'll try to update the answer accordingly :) – Moritz Roessler Feb 23 '14 at 19:35

1 Answers1

10

[...] It looks like the primitive number has methods

The primitive, does not actually has its own properties. It gets coerced to an object in order to be able to access "its" properties. The coerced object is not accessable outside the called Method *(In strict mode even not inside the method)*. As such, the referenced variable is always a primitive.

Consider this simple example:

Number.prototype.myTypeInAMethod = function () {
   console.log (typeof this.valueOf ()) //"number" => The primitive is wrapped in an object. 
   return typeof this;
}

var num = 123;
typeof num; //number
num.myTypeInAMethod () //object

side note: In ES5s strict mode,this would be a primitive and the type would be number

Since the variable num is a primitive, you can not assign values to it.

num.foo = "bar";
num.foo //undefined

If you instead create a number (or string) via its object constructor, its type indeed is an object. A quick check by adding a property shows it can actually be assigned.

var objNum = new Number(123);
typeof objNum ; //"object"
objNum.foo = "bar";
objNum.foo //"bar"

So what is the magic behind this? How JavaScript treats objects versus primitive types?

This process is described in ES5 §8.7.1 GetValue

For an object:

  • If Type(V) is not Reference, return V.
  • Let base be the result of calling GetBase(V).
  • If IsUnresolvableReference(V), throw a ReferenceError exception.
  • If IsPropertyReference(V), then
    • If HasPrimitiveBase(V) is false, then let get be the [[Get]] internal method of base, otherwise let get be the special [[Get]] internal method defined below.
    • Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.
  • Else, base must be an environment record.
    • Return the result of calling the GetBindingValue (see 10.2.1) concrete method of base passing GetReferencedName(V) and IsStrictReference(V) as arguments.

For a primitive:

The following [[Get]] internal method is used by GetValue when V is a property reference[1] with a primitive base value. It is called using base as its this value and with property P as its argument. The following steps are taken:

  • Let O be ToObject(base).
  • Let desc be the result of calling the [[GetProperty]] internal method of O with property name P.
  • If desc is undefined, return undefined.
  • If IsDataDescriptor(desc) is true, return desc.[[Value]].
  • Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
  • If getter is undefined, return undefined.
  • Return the result calling the [[Call]] internal method of getter providing base as the this value and providing no arguments.

NOTE The object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of the object. The only situation where such an actual property access that uses this internal method can have visible effect is when it invokes an accessor function.

[1] IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.

Moritz Roessler
  • 8,542
  • 26
  • 51
  • We are checking the 'typeof' on the 'method' we gave it - which is correct as an 'object' - but the 'str' 'type' is still a string. The 'type' is not coerced / changed. – Rob Sedgwick Feb 21 '14 at 11:42
  • @RobSedgwick Thanks :) I tried to change the wording a bit, to make it more clear that a primitive stays a primitive. You're welcome to rephrase the answer if you have a better formulation, as english is not my native language. – Moritz Roessler Feb 21 '14 at 12:05
  • 1
    I think yor answer is great. I added that comment as it looks like the OP is getting confused between a 'number' **value** ( which is type of 'number') and the 'number' class ( object ) which has methods. As your answer shows we can use a string value and the string class 'new String("string")'. – Rob Sedgwick Feb 21 '14 at 12:29
  • @RobSedgwick Yes, that's a good point. I updated the answer to answer the parts of the question more directly and used a number primitive as example. Thanks for the Feedback :) – Moritz Roessler Feb 21 '14 at 16:15
  • @C5H8NNaO4 Thank You for an excellent answer! I am definitely marking it as accepted. However I personally believe that the second part (copied from ES5 spec.) could be explained in greater detail for less experienced readers, but I will leave that on you :-). – Roman Mazur Feb 24 '14 at 16:10
  • @C5H8NNaO4 it's seems like you know how works GetValue. Can you explain about him? – MaximPro Aug 23 '17 at 11:37