Primitives vs. Objects
Primitive values are not objects. They can, however, act like objects. JavaScript does this by wrapping the primitive in an object wrapper.
var str1 = 'not an object' , str2 = String('not an object'), str3 = new String('is an object'); str1.length; // 13 - primitive acting like an object 'hi'.length; // 2 - primitive acting like an object
NOTE: You can think of primitive values as being atomic or irreducible in relation to objects. Objects can be broken down into properties and values, while primitives are literal values themselves.
It is, therefore, a common misconception that everything in JavaScript is an object. Not everything in JavaScript is an object, but rather, everything in JavaScript can act like an object.
The
String()
,Number()
andBoolean()
constructors return an object when called with thenew
operator and a primitive when called without thenew
operator.var str1 = 'not an object' , // returns a primitive (String() not called) str2 = String('not an object'), // returns a primitive str3 = new String('is an object'); // returns an object
NOTE: The String() constructor is not called for
str1
. JavaScript creates this String literal using internal native code.When dealing with primitives (e.g.
String
,Number
orBoolean
), an object is only created when thenew
operator is used or when the primitive is treated like an object.An object is not created when a primitive value is defined using literal syntax (e.g.
var str = 'hi'
) or a constructor without thenew
operator (e.g.var str = String('hi')
). Instead, the JavaScript engine creates a primitive value.However, an object is created once the primitive is treated like an object (e.g. by invoking a method on the primitive or by adding a property to ). When this happens, JavaScript creates an object wrapper that temporarily wraps around the primitive, allowing for it to be treated as an object. The object wrapper is then discarded after the expression is complete, bringing the value back to it's original primitive state.
Compare the following:
// primitive var s1 = 'hi'; typeof s1; // "string" s1; // hi
// primitive var s2 = String('hi'); typeof s2; // "string" s2; // hi
// object var s3 = new String('hi'); typeof s3; // "object" s3; // String {0: "h", 1: "i", length: 2, [[PrimitiveValue]]: "hi"}
Let's see what happens when we treat a primitive like an object:
// add a method to s1 and invoke it s1.func = function() {return 'myFunc1'}; s1.func; // undefined - (?) didn't we just define it? s1.func(); // error: [1]
// add a method to s2 and invoke it s2.func = function() {return 'myFunc2'}; s2.func; // undefined s2.func(); // error: [1]
// add a method to s3 and invoke it s3.func = function() {return 'myFunc3'}; s3.func; // function() s3.func(); // "myFunc3"
/* Footnotes: [1] At this point, the object wrapper around s1 has been discarded, so s1.func, the function assigned to s1's object wrapper, no longer exists. */
In the code below, all of the primitive values (except for
null
andundefined
) are wrapped by object wrappers. ThetoString()
method is then called on each object wrapper. The objects revert back to primitive values once the method is invoked and returned.// primitives var nul = null, undef = undefined, str1 = "foo", str2 = String('foo'), num1 = 10, num2 = Number('10'), bool1 = true, bool2 = Boolean('true'); // toString() called on each primitive console.log(str1.toString(), str2.toString()); // foo foo console.log(num1.toString(), num2.toString()); // 10 10 console.log(bool1.toString(), bool2.toString()); // true true console.log(myNull.toString()); // error [1] console.log(myUndefined.toString()); // error [1] /* Footnotes: [1] An error is raised because null and undefined do not have constructors, and therefore, do not convert to objects. */
NOTE: When the values
String
,Number
, andBoolean
are either created using thenew
keyword or converted to objects behind the scenes, the values continue to be stored and copied by value. So, although primitive values can be treated like objects, they do not take on the quality of being copied by reference.Primitives are copied-by-value.
var bob = 'Bob', sam = bob; bob; // Bob sam; // Bob sam = 'Sam'; bob; // Bob sam; // Sam
Changes made to
bob
affectsam
.Objects are copied-by-reference.
var bob = {name: 'Bob'}, sam = bob; bob.name; // Bob sam.name; // Bob sam.name = 'Sam'; bob.name; // Sam sam.name; // Sam
Changes made to
bob
do not affectsam
.- An object is not stored on the stack directly—the reference to that object is stored on the stack. The object itself is stored within in the heap.
var obj = {}; obj; // obj -> [memory address] -> Object {}
Compare this to referencing primitives:
To better understand copy-by-value vs. copy-by-reference, it is important to understand that when something is copied, whether a primitive or an object, the value literals themselves are being copied. In the case of primitives, the literal value is copied, hence copy-by-value. In the case of objects, the object's memory address (or reference) is copied, hence copy-by-reference.var str = 'hi'; str; // str -> 'hi'
var obj1 = {}, obj2 = obj1, prim1 = 'primitive', prim2 = prim1;
key | value ------- | -------------------------------- 'obj1' | 0x7fff9575c05f (address literal) 'obj2' | 0x7fff9575c05f (address literal) 'prim1' | 'primitive' (string literal) 'prim2' | 'primitive' (string literal)
==
compares value.===
compares value and type.var n1 = 10 , // primitive n2 = Number(10), // primitive n3 = new Number(10), // object n4 = n3; // object n1 == n2; // true n1 === n2; // true n2 == n3; // true n2 === n3; // false - primitive ≠ object n3 == n4; // true n3 === n4; // true
According to the
===
operator,n2
andn3
have the same value, but are of different types.n2
is a number primitive, andn3
is anNumber
object.Both primitives and objects are compared by value (the value stored within the variable). But wait, aren't objects compared by reference? Yes, remember that the value of object variables is the reference to that object in memory.
// primitives compared by value var prim1 = 10, // prim1 -> 10 prim2 = 10; // prim2 -> 10 prim1 == prim2; // true prim1 === prim2; // true
// objects compared by value (of reference) var obj1 = {}, // obj1 -> [0x00000] obj2 = {}, // obj2 -> [0x11111] obj3 = obj1; // obj3 -> obj1 -> [0x00000] obj1 === obj2; // false - [0x00000] ≠ [0x11111] obj3 === obj1; // true - [0x00000] = [0x00000]
All objects have a constructor property somewhere in their prototype chain. This property may or may not be the object's own property. Its value is the constructor function that created the object.
// A generic object created by the Object() constructor var foo = {}; // foo's insides look something like: Object { __proto__: Object { constructor: function Object() { ... }, // constructor property // more properties } };
// A String object created by the String() constructor var str = new String('hi'); // str's insides look something like: String { __proto__: String { constructor: function String() { ... }, // constructor property // more properties }, // more properties };
var foo = {}; foo.constructor; // [1][2] function Object() foo.constructor(); // [3] Object {} /* Footnotes: [1] Object() is the contructor that created foo. [2] Notice that Object is a function (contructor function to be exact) that can be invoked. String, Array, and the other data types are also functions. [3] foo's constructor is invoked and a new Object is returned. */
The same goes for user-defined objects:
var CustomConstructor = function CustomConstructor() {this.message = 'Wow!'}, instanceOfCustomObject = new CustomConstructor(); // true instanceOfCustomObject.constructor === CustomConstructor; // function CustomConstructor() {this.message = 'Wow!'} instanceOfCustomObject.constructor;
NOTE: A JavaScript object is not necessarily an instance of
Object()
. An object created by theObject()
constructor is a generic object. A JavaScript object, however, could be an instance ofArray()
,Number()
or some user-defined object.