In JavaScript, objects are king: Almost everything is an object or acts like an object. Understand objects and you will understand JavaScript. So let's examine the creation of objects in JavaScript.
An object is just a container for a collection of named values (aka properties). Before we look at any JavaScript code, let's first reason this out. Take myself, for example. Using plain language, we can express in a table, a "cody":
Property | Property Value |
living | True |
age | 33 |
gender | Male |
The word "cody" in the table is just a label for the group of property names and corresponding values that make up exactly what a cody is. As you can see from the table, I am living, 33, and a male.
JavaScript, however, does not speak in tables. It speaks in objects, which are similar to the parts contained in the "cody” table. Translating the cody table into an actual JavaScript object would look like this:
Sample: sample1.html
<!DOCTYPE html><html lang="en"><body><script> // Create the cody object var cody = new Object(); // then fill the cody object with properties (using dot notation). cody.living = true; cody.age = 33; cody.gender = 'male'; console.log(cody); // Logs Object {living = true, age = 33, gender = 'male'} </script></body></html>
Keep this at the forefront of your mind: objects are really just containers for properties, each of which has a name and a value. This notion of a container of properties with named values (i.e. an object) is used by JavaScript as the building blocks for expressing values in JavaScript. The cody object is a value which I expressed as a JavaScript object by creating an object, giving the object a name, and then giving the object properties.
Up to this point, the cody object we are discussing has only static information. Since we are dealing with a programing language, we want to program our cody object to actually do something. Otherwise, all we really have is a database akin to JSON. In order to bring the cody object to life, I need to add a property method. Property methods perform a function. To be precise, in JavaScript, methods are properties that contain a Function()
object, whose intent is to operate on the object the function is contained within.
If I were to update the cody table with a getGender method, in plain English it would look like this:
Property | Property Value |
living | True |
age | 33 |
gender | Male |
getGender | return the value of gender |
Using JavaScript, the getGender
method from the updated cody table would look like so:
Sample: sample2.html
<!DOCTYPE html><html lang="en"><body><script> var cody = new Object(); cody.living = true; cody.age = 33; cody.gender = 'male'; cody.getGender = function () { return cody.gender; }; console.log(cody.getGender()); // Logs 'male'. </script></body></html>
The getGender
method, a property of the cody object, is used to return one of cody’s other property values: the value "male" stored in the gender property. What you must realize is that without methods, our object would not do much except store static properties.
The cody object we have discussed thus far is what is known as an Object()
object. We created the cody object using a blank object that was provided to us by invoking the Object()
constructor function. Think of constructor functions as a template or cookie cutter for producing predefined objects. In the case of the cody object, I used the Object()
constructor function to produce an empty object which I named cody. Because cody is an object constructed from the Object()
constructor, we call cody an Object()
object. What you really need to understand, beyond the creation of a simple Object()
object like cody, is that the majority of values expressed in JavaScript are objects (primitive values like "foo", 5, and true are the exception but have equivalent wrapper objects).
Consider that the cody object created from the Object()
constructor function is not really different from a string object created via the String()
constructor function. To drive this fact home, examine and contrast the following code:
Sample: sample3.html
<!DOCTYPE html><html lang="en"><body><script> var myObject = new Object(); // Produces an Object() object. myObject['0'] = 'f'; myObject['1'] = 'o'; myObject['2'] = 'o'; console.log(myObject); // Logs Object { 0="f", 1="o", 2="o"} var myString = new String('foo'); // Produces a String() object. console.log(myString); // Logs foo { 0="f", 1="o", 2="o"} </script></body></html>
As it turns out, myObject and myString are both . . . objects! They both can have properties, inherit properties, and are produced from a constructor function. The myString variable containing the 'foo' string value seems to be as simple as it goes, but amazingly it’s got an object structure under its surface. If you examine both of the objects produced you will see that they are identical objects in substance but not in type. More importantly, I hope you begin to see that JavaScript uses objects to express values.
You might find it odd to see the string value 'foo' in object form because typically a string is represented in JavaScript as a primitive value (e.g., var myString = 'foo';
). I specifically used a string object value here to highlight that anything can be an object, including values that we might not typically think of as an object (e.g., string, number, Boolean). Also, I think this helps explain why some say that everything in JavaScript can be an object.
JavaScript bakes the String()
and Object()
constructor functions into the language itself to make the creation of a String()
object and Object()
object trivial. But you, as a coder of the JavaScript language, can also create equally powerful constructor functions. In the following sample, I demonstrate this by defining a non-native custom Person()
constructor function so that I can create people from it.
Sample: sample4.html
<!DOCTYPE html><html lang="en"><body><script> // Define Person constructor function in order to create custom Person() objects later. var Person = function (living, age, gender) { this.living = living; this.age = age; this.gender = gender; this.getGender = function () { return this.gender; }; }; // Instantiate a Person object and store it in the cody variable. var cody = new Person(true, 33, 'male'); console.log(cody); /* The String() constructor function that follows, having been defined by JavaScript, has the same pattern. Because the string constructor is native to JavaScript, all we have to do to get a string instance is instantiate it. But the pattern is the same whether we use native constructors like String() or user-defined constructors like Person(). */ // Instantiate a String object stored in the myString variable. var myString = new String('foo'); console.log(myString); </script></body></html>
The user-defined Person()
constructor function can produce Person objects, just as the native String()
constructor function can produce string objects. The Person()
constructor is no less capable, and is no more or less malleable, than the native String()
constructor or any of the native constructors found in JavaScript.
Remember how the cody object we first looked at was produced from an Object()
. It’s important to note that the Object()
constructor function and the new Person()
constructor shown in the previous code example can give us identical outcomes. Both can produce an identical object with the same properties and property methods. Examine the two sections of code that follow, showing that codyA
and codyB
have the same object values even though they are produced in different ways.
Sample: sample5.html
<!DOCTYPE html><html lang="en"><body><script> // Create a codyA object using the Object() constructor. var codyA = new Object(); codyA.living = true; codyA.age = 33; codyA.gender = 'male'; codyA.getGender = function () { return codyA.gender; }; console.log(codyA); // Logs Object {living=true, age=33, gender="male", ...} /* The same cody object is created below, but instead of using the native Object() constructor to create a one-off cody, we first define our own Person() constructor that can create a cody object (and any other Person object we like) and then instantiate it with "new". */ var Person = function (living, age, gender) { this.living = living; this.age = age; this.gender = gender; this.getGender = function () { return this.gender; }; }; var codyB = new Person(true, 33, 'male'); console.log(codyB); // Logs Object {living=true, age=33, gender="male", ...} </script></body></html>
The main difference between the codyA and codyB objects is not found in the object itself, but in the constructor functions used to produce the objects. The codyA object was produced using an instance of the Object()
constructor. The Person()
constructor produced codyB, but can also be used as a powerful, centrally defined object "factory" to be used for creating more Person()
objects. Crafting your own constructors for producing custom objects also sets up prototypal inheritance for Person()
instances.
Both solutions resulted in the same complex object being created. Its these two patterns that are most commonly used for constructing objects.
JavaScript is really just a language that is prepackaged with a few native object constructors used to produce complex objects which express a very specific type of value (e.g., numbers, strings, functions, objects, arrays, etc.), as well as the raw materials via Function()
objects for crafting user-defined object constructors (e.g., Person()
). The end resultno matter the pattern for creating the objectis typically the creation of a complex object.
Understanding the creation, nature, and usage of objects and their primitive equivalents is the focus of the rest of this book.
JavaScript Constructors Create and Return Object Instances
The role of a constructor function is to create multiple objects that share certain qualities and behaviors. Basically, a constructor function is a cookie cutter for producing objects that have default properties and property methods.
If you said, "A constructor is nothing more than a function," then I would reply, "You are correctunless that function is invoked using the new
keyword." (For example, new String('foo')
). When this happens, a function takes on a special role, and JavaScript treats the function as special by setting the value of this
for the function to the new object that is being constructed. In addition to this special behavior, the function will return the newly created object (i.e. this
) by default instead of the value false
. The new object that is returned from the function is considered to be an instance of the constructor function that constructs it.
Consider the Person()
constructor again, but this time read the comments in the following code sample carefully, as they highlight the effect of the new
keyword.
Sample: sample6.html
<!DOCTYPE html><html lang="en"><body><script> /* Person is a constructor function. It was written with the intent of being used with the new keyword. */ var Person = function Person(living, age, gender) { // "this" below is the new object that is being created (i.e. this = new Object();) this.living = living; this.age = age; this.gender = gender; this.getGender = function () { return this.gender; }; // When the function is called with the new keyword, "this" is returned instead of false. }; // Instantiate a Person object named cody. var cody = new Person(true, 33, 'male'); // cody is an object and an instance of Person() console.log(typeof cody); // Logs object. console.log(cody); // Logs the internal properties and values of cody. console.log(cody.constructor); // Logs the Person() function. </script></body></html>
The sample6.html code leverages a user-defined constructor function (i.e. Person()
) to create the cody object. This is no different from the Array()
constructor creating an Array()
object (e.g., new Array()
) in the following code.
Sample: sample7.html
<!DOCTYPE html><html lang="en"><body><script> // Instantiate an Array object named myArray. var myArray = new Array(); // myArray is an instance of Array. // myArray is an object and an instance of the Array() constructor. console.log(typeof myArray); // Logs object! What? Yes, arrays are a type of object. console.log(myArray); // Logs [ ] console.log(myArray.constructor); // Logs Array() </script></body></html>
In JavaScript, most values (excluding primitive values) involve objects being created, or instantiated, from a constructor function. An object returned from a constructor is called an instance. Make sure you are comfortable with these semantics, as well as the pattern of leveraging constructors to produce objects.
The Native JavaScript Object Constructors
The JavaScript language contains nine native (or built-in) object constructors. These objects are used by JavaScript to construct the language, and by "construct" I mean these objects are used to express object values in JavaScript code, as well as orchestrate several features of the language. Thus, the native object constructors are multifaceted in that they produce objects, but are also leveraged in facilitating many of the languages programming conventions. For example, functions are objects created from the Function()
constructor, but are also used to create other objects when called as constructor functions using the new
keyword.
The nine native object constructors that come prepackaged with JavaScript are:
Number()
String()
Boolean()
Object()
Array()
Function()
Date()
RegExp()
Error()
JavaScript is mostly constructed from these nine objects (as well as string, number, and Boolean primitive values). Understanding these objects in detail is key to taking advantage of JavaScript’s unique programming power and language flexibility.
The Math
object is the oddball here. It's a static object rather than a constructor function, meaning you cant do this: var x = new Math()
. But you can use it as if it has already been instantiated (e.g., Math.PI
). Truly, Math
is just an object namespace set up by JavaScript to house math functions.
The native objects are sometimes referred to as "global objects" since they are the objects that JavaScript has made natively available for use. Do not confuse the term global object with the "head" global object that is the topmost level of the scope chain, for example, the window
object in all web browsers.
The Number()
, String()
, and Boolean()
constructors not only construct objects; they also provide a primitive value for a string, number, and Boolean, depending upon how the constructor is leveraged. If you call these constructors directly, then a complex object is returned. If you simply express a number, string, or Boolean value in your code (primitive values like 5, "foo", and true
), then the constructor will return a primitive value instead of a complex object value.
User-Defined/Non-Native Object Constructor Functions
As you saw with the Person()
constructor, we can make our own constructor functions from which we can produce not just one, but multiple custom objects.
In the following sample, I present the familiar Person()
constructor function:
Sample: sample8.html
<!DOCTYPE html><html lang="en"><body><script> var Person = function (living, age, gender) { this.living = living; this.age = age; this.gender = gender; this.getGender = function () { return this.gender; }; }; var cody = new Person(true, 33, 'male'); console.log(cody); // Logs Object {living=true, age=33, gender="male", ...} var lisa = new Person(true, 34, 'female'); console.log(lisa); // Logs Object {living=true, age=34, gender="female", ...} </script></body></html>
As you can see, by passing unique parameters and invoking the Person()
constructor function, you could easily create a vast number of unique people objects. This can be pretty handy when you need more than two or three objects that possess the same properties, but with different values. Come to think of it, this is exactly what JavaScript does with the native objects. The Person()
constructor follows the same principles as the Array()
constructor. So new Array('foo','bar')
is really not that different than new Person(true, 33, 'male')
. Creating your own constructor functions is just using the same pattern that JavaScript itself uses for its own native constructor functions.
It is not required, but when creating custom constructor functions intended to be used with the new operator, its best practice to make the first character of the constructor name uppercase: Person()
rather than person()
.
One tricky thing about constructor functions is the use of the this
value inside of the function. Remember, a constructor function is just a cookie cutter. When used with the new
keyword, it will create an object with properties and values defined inside of the constructor function. When new
is used, the value this
literally means the new object or instance that will be created based on the statements inside the constructor function. On the other hand, if you create a constructor function and call it without the use of the new
keyword, the this
value will refer to the "parent" object that contains the function. More detail about this topic can be found in Chapter 6.
It's possible to forgo the use of the new keyword and the concept of a constructor function by explicitly having the function return an object. The function would have to be written explicitly to build an Object()
object and return it: var myFunction = function() {return {prop: val}};
.
Instantiating Constructors Using the new
Operator
A constructor function is basically a cookie-cutter template used to create pre-configured objects. Take String()
for example. This function, when used with the new
operator (new String('foo')
), creates a string instance based on the String()
"template.” Let's look at an example.
Sample: sample9.html
<!DOCTYPE html><html lang="en"><body><script> var myString = new String('foo'); console.log(myString); // Logs foo {0 = "f", 1 = "o", 2 = "o"} </script></body></html>
In this snippet, we created a new string object that is an instance of the String()
constructor function. Just like that, we have a string value expressed in JavaScript.
I'm not suggesting that you use constructor functions instead of their literal/primitive equivalentslike var string="foo";
. I am, however, suggesting that you understand what is going on behind literal/primitive values.
As previously mentioned, the JavaScript language has the following native predefined constructors: Number()
, String()
, Boolean()
, Object()
, Array()
, Function()
, Date()
, RegExp()
, and Error()
. We can instantiate an object instance from any of these constructor functions by applying the new operator. In the following sample, I construct these nine native JavaScript objects.
Sample: sample10.html
<!DOCTYPE html><html lang="en"><body><script> // Instantiate an instance for each native constructor using the new keyword. var myNumber = new Number(23); var myString = new String('male'); var myBoolean = new Boolean(false); var myObject = new Object(); var myArray = new Array('foo', 'bar'); var myFunction = new Function("x", "y", "return x*y"); var myDate = new Date(); var myRegExp = new RegExp('\bt[a-z]+\b'); var myError = new Error('Darn!'); // Log/verify which constructor created the object. console.log(myNumber.constructor); // Logs Number() console.log(myString.constructor); // Logs String() console.log(myBoolean.constructor); // Logs Boolean() console.log(myObject.constructor); // Logs Object() console.log(myArray.constructor); // Logs Array() in modern browsers. console.log(myFunction.constructor); // Logs Function() console.log(myDate.constructor); // Logs Date() console.log(myRegExp.constructor); // Logs RegExp() console.log(myError.constructor); // Logs Error() </script></body></html>
By using the new
operator, we are telling the JavaScript interpreter that we would like an object that is an instance of the corresponding constructor function. For example, in the code sample, the Date()
constructor function is used to create date objects. The Date()
constructor function is a cookie cutter for date objects. That is, it produces date objects from a default pattern defined by the Date()
constructor function.
At this point, you should be well acquainted with creating object instances from native constructor functions (e.g., new String('foo')
) and user-defined constructor functions (e.g, new Person(true, 33, 'male')
).
Keep in mind that Math
is a static objecta container for other methodsand is not a constructor that uses the new
operator.
Creating Shorthand or Literal Values from Constructors
JavaScript provides shortcutscalled "literals" for manufacturing most of the native object values without having to use new Foo()
or new Bar()
. For the most part, the literal syntax accomplishes the same thing as using the new
operator. The exceptions are: Number()
, String()
, and Boolean()
see the notes after the following sample.
If you come from other programming backgrounds, you are likely more familiar with the literal way of creating objects. In the following sample, I instantiate the native JavaScript constructors using the new
operator and then create corresponding literal equivalents.
Sample: sample11.html
<!DOCTYPE html><html lang="en"><body><script> var myNumber = new Number(23); // An object. var myNumberLiteral = 23; // Primitive number value, not an object. var myString = new String('male'); // An object. var myStringLiteral = 'male'; // Primitive string value, not an object. var myBoolean = new Boolean(false); // An object. var myBooleanLiteral = false; // Primitive boolean value, not an object. var myObject = new Object(); var myObjectLiteral = {}; var myArray = new Array('foo', 'bar'); var myArrayLiteral = ['foo', 'bar']; var myFunction = new Function("x", "y", "return x*y"); var myFunctionLiteral = function (x, y) { return x * y }; var myRegExp = new RegExp('\bt[a-z]+\b'); var myRegExpLiteral = /\bt[a-z]+\b/; // Verify that literals are created from same constructor. console.log(myNumber.constructor, myNumberLiteral.constructor); console.log(myString.constructor, myStringLiteral.constructor); console.log(myBoolean.constructor, myBooleanLiteral.constructor); console.log(myObject.constructor, myObjectLiteral.constructor); console.log(myArray.constructor, myArrayLiteral.constructor); console.log(myFunction.constructor, myFunctionLiteral.constructor); console.log(myRegExp.constructor, myRegExpLiteral.constructor); </script></body></html>
What you need to take away here is the fact that, in general, using literals simply conceals the underlying process identical to using the new
operator. Maybe more importantly, its much more convenient!
Okay, things are a little more complicated with respect to the primitive string, number, and Boolean values. In these cases, literal values take on the characteristics of primitive values rather than complex object values. See the notes that follow.
When using literal values for String()
, Number()
, and Boolean()
, an actual complex object is never created until the value is treated as an object. In other words, you are dealing with a primitive data type until you attempt to use methods or retrieve properties associated with the constructor (e.g., var charactersInFoo = 'foo'.length
). When this happens, JavaScript creates a wrapper object for the literal value behind the scenes, allowing the value to be treated as an object. Then, after the method is called, JavaScript discards the wrapper object and the value returns to a literal type. This is why string, number, and Boolean are considered primitive (or simple) data types. I hope this clarifies the misconception "everything in JavaScript is an object" from the concept "everything in JavaScript can act like an object.”
Primitive (aka Simple) Values
The JavaScript values 5, 'foo', true
, and false
, as well as null
and undefined
, are considered primitive because they are irreducible. That is, a number is a number, a string is a string, a Boolean is either true
or false
, and null
and undefined
are just that, null
and undefined
. These values are inherently simple and do not represent values that can be made up of other values.
Examine the following code and ask yourself if the string, number, Boolean, null
, and undefined
values could be more complex. Contrast this to what you know of an Object()
instance, Array()
instance, or really any complex object.
Sample: sample12.html
<!DOCTYPE html><html lang="en"><body><script> var myString = 'string' var myNumber = 10; var myBoolean = false; // Could be true or false, but that is it. var myNull = null; var myUndefined = undefined; console.log(myString, myNumber, myBoolean, myNull, myUndefined); /* Consider that a complex object like array or object can be made up of multiple primitive values, and thus becomes a complex set of multiple values. */ var myObject = { myString: 'string', myNumber: 10, myBoolean: false, myNull: null, myUndefined: undefined }; console.log(myObject); var myArray = ['string', 10, false, null, undefined]; console.log(myArray); </script></body></html>
Quite simply, primitive values represent the lowest form (i.e. simplest) of data and information available in JavaScript.
As opposed to creating values with literal syntax, when a String()
, Number()
, or Boolean()
value is created using the new keyword, the object created is actually a complex object.
Its critical that you understand the fact that the String()
, Number()
, and Boolean()
constructors are dual-purpose constructors used to create literal/primitive values as well as complex values. These constructors do not always return objects, but instead, when used without the "new
" operator, can return a primitive representation of the actual complex object value.
Primitive Values Are Not Objects
The null
and undefined
values are such trivial values that they do not require a constructor function, nor the use of the new
operator to establish them as a JavaScript value. To use null
or undefined
, all you do is use them as if they were an operator. The remaining primitive valuesstring, number, and Booleanwhile technically returned from a constructor function, are not objects.
In the following sample, I contrast the difference between primitive values and the rest of the native JavaScript objects.
Sample: sample13.html
<!DOCTYPE html><html lang="en"><body><script> // No object is created when producing primitive values; notice no use of the "new" keyword. var primitiveString1 = "foo"; var primitiveString2 = String('foo'); var primitiveNumber1 = 10; var primitiveNumber2 = Number('10'); var primitiveBoolean1 = true; var primitiveBoolean2 = Boolean('true'); // Confirm the typeof is not object. console.log(typeof primitiveString1, typeof primitiveString2); // Logs 'string,string'. console.log(typeof primitiveNumber1, typeof primitiveNumber2); // Logs 'number,number'. console.log(typeof primitiveBoolean1, typeof primitiveBoolean2); // Logs 'Boolean,Boolean'. // Using a constructor and the "new" keyword for creating objects. var myNumber = new Number(23); var myString = new String('male'); var myBoolean = new Boolean(false); var myObject = new Object(); var myArray = new Array('foo', 'bar'); var myFunction = new Function("x", "y", "return x * y"); var myDate = new Date(); var myRegExp = new RegExp('\\bt[a-z]+\\b'); var myError = new Error('Darn!'); // Logs 'object object object object object function object function object'. console.log( typeof myNumber, typeof myString, typeof myBoolean, typeof myObject, typeof myArray, typeof myFunction, // BE AWARE typeof returns function for all function objects. typeof myDate, typeof myRegExp, // BE AWARE typeof returns function for RegExp() typeof myError ); </script></body></html>
What I would like you to learn from the previous code example is that primitive values are not objects. Primitive values are special in that they are used to represent simple values.
How Primitive Values Are Stored/Copied in JavaScript
It is extremely important to understand that primitive values are stored and manipulated at "face value. It might sound simple, but this means that if I store the string value "foo" in a variable called myString
, then the value "foo" is literally stored in memory as such. Why is this important? Once you begin manipulating (e.g., copying) values, you have to be equipped with this knowledge, because primitive values are copied literally.
In the following example, we store a copy of the myString
value ('foo')
in the variable myStringCopy, and its value is literally copied. Even if we change the original value, the copied value, referenced by the variable myStringCopy
, remains unchanged.
Sample: sample14.html
<!DOCTYPE html><html lang="en"><body><script> var myString = 'foo' // Create a primitive string object. var myStringCopy = myString; // Copy its value into a new variable. var myString = null; // Manipulate the value stored in the myString variable. /*The original value from myString was copied to myStringCopy. This is confirmed by updating the value of myString then checking the value of myStringCopy.*/ console.log(myString, myStringCopy); // Logs 'null foo' </script></body></html>
The concept to take away here is that primitive values are stored and manipulated as irreducible values. Referring to them transfers their value. In the previous example, we copied, or cloned, the myString
value to the variable myStringCopy
. When we updated the myString
value, the myStringCopy
value still had a copy of the old myString
value. Remember this and contrast the mechanics here with complex objects (discussed in the following section).
Primitive Values Are Equal by Value
Primitives can be compared to see if their values are literally the same. As logic would suggest, if you compare a variable containing the numeric value 10 with another variable containing the numeric value 10, JavaScript will consider these equal because 10 is the same as 10 (i.e. 10 === 10
). The same, of course, would apply if you compare the primitive string 'foo' to another primitive string with a value of 'foo'. The comparison would say that they are equal to each other based on their value (i.e. 'foo' === 'foo'
).
In the following code, I demonstrate the "equal by value" concept using primitive numbers, as well as contrast this with a complex number object.
Sample: sample15.html
<!DOCTYPE html><html lang="en"><body><script> var price1 = 10; var price2 = 10; var price3 = new Number('10'); // A complex numeric object because new was used. var price4 = price3; console.log(price1 === price2); // Logs true. /* Logs false because price3 contains a complex number object and price 1 is a primitive value. */ console.log(price1 === price3); // Logs true because complex values are equal by reference, not value. console.log(price4 === price3); // What if we update the price4 variable to contain a primitive value? price4 = 10; console.log(price4 === price3); // Logs false: price4 is now primitive rather than complex. </script></body></html>
The concept to take away here is that primitives, when compared, will check to see if the expressed values are equal. When a string, number, or Boolean value is created using the new
keyword (e.g., new Number('10')
), the value is no longer primitive. As such, comparison does not work the same as if the value had been created via literal syntax. This is not surprising, given that primitive values are stored by value (i.e. does 10 === 10
), while complex values are stored by reference (i.e. does price3 and price4 contain a reference to the same value).
The String, Number, and Boolean Primitive Values Act Like Objects When Used Like Objects
When a primitive value is used as if it were an object created by a constructor, JavaScript converts it to an object in order to respond to the expression at hand, but then discards the object qualities and changes it back to a primitive value. In the code that follows, I take primitive values and showcase what happens when the values are treated like objects.
Sample: sample16.html
<!DOCTYPE html><html lang="en"><body><script> // Produce primitive values. var myNull = null; var myUndefined = undefined; var primitiveString1 = "foo"; var primitiveString2 = String('foo'); // Did not use new, so we get primitive. var primitiveNumber1 = 10; var primitiveNumber2 = Number('10'); // Did not use new, so we get primitive. var primitiveBoolean1 = true; var primitiveBoolean2 = Boolean('true'); // Did not use new, so we get primitive. /* Access the toString() property method (inherited by objects from object.prototype) to demonstrate that the primitive values are converted to objects when treated like objects. */ // Logs "string string" console.log(primitiveString1.toString(), primitiveString2.toString()); // Logs "number number" console.log(primitiveNumber1.toString(), primitiveNumber2.toString()); // Logs "boolean boolean" console.log(primitiveBoolean1.toString(), primitiveBoolean2.toString()); /* This will throw an error and not show up in Firebug Lite, as null and undefined do not convert to objects and do not have constructors. */ console.log(myNull.toString()); console.log(myUndefined.toString()); </script></body></html>
In this code example, all of the primitive values (except null
and undefined
) are converted to objects, so as to leverage the toString()
method, and then are returned to primitive values once the method is invoked and returned.
Complex (aka Composite) Values
The native object constructors Object()
, Array()
, Function()
, Date()
, Error()
, and RegExp()
are complex because they can contain one or more primitive or complex values. Essentially, complex values can be made up of many different types of JavaScript objects. It could be said that complex objects have an unknown size in memory because complex objects can contain any value and not a specific known value. In the following code, we create an object and an array that houses all of the primitive objects.
Sample: sample17.html
<!DOCTYPE html><html lang="en"><body><script> var object = { myString: 'string', myNumber: 10, myBoolean: false, myNull: null, myUndefined: undefined }; var array = ['string', 10, false, null, undefined]; /* Contrast this to the simplicity of the primitive values below. In a primitive form, none of the values below can be more complex than what you see while complex values can encapsulate any of the JavaScript values (seen above). */ var myString = 'string'; var myNumber = 10; var myBoolean = false; var myNull = null; var myUndefined = undefined; </script></body></html>
The concept to take away here is that complex values are a composite of values and differ in complexity and composition to primitive values.
The term "complex object" has also been expressed in other writings as "composite objects" or "reference types.” If it's not obvious, all these names describe the nature of a JavaScript value excluding primitive values. Primitive values are not "referenced by value" and cannot represent a composite (i.e. a thing made up of several parts or elements) of other values, while complex objects are "referenced by value" and can contain or encapsulate other values.
How Complex Values Are Stored/Copied in JavaScript
It is extremely important to understand that complex values are stored and manipulated by reference. When creating a variable containing a complex object, the value is stored in memory at an address. When you reference a complex object, you’re using its name (i.e. variable or object property) to retrieve the value at that address in memory. The implications are significant when you consider what happens when you attempt to copy a complex value. In the next sample, we create an object stored in the variable myObject
. The value in myObject
is then copied to the variable copyOfMyObject
. Really, it is not a copy of the object—more like a copy of the address of the object.
Sample: sample18.html
<!DOCTYPE html><html lang="en"><body><script> var myObject = {}; var copyOfMyObject = myObject; // Not copied by value, just the reference is copied. myObject.foo = 'bar'; // Manipulate the value stored in myObject. /* If we log myObject and copyOfMyObject, they will have a foo property because they reference the same object. */ console.log(myObject, copyOfMyObject); // Logs 'Object { foo="bar"} Object { foo="bar"}' </script></body></html>
What you need to realize is that, unlike primitive values that would copy a value, objects (aka complex values) are stored by reference. As such, the reference (aka address) is copied, but not the actual value. This means that objects are not copied at all. Like I said, what is copied is the address or reference to the object in the memory stack. In our code example, myObject
and copyOfMyObject
point to the same object stored in memory.
The idea to take away here is that when you change a complex valuebecause it is stored by referenceyou change the value stored in all variables that reference the complex value. In our code example, both myObject
and copyOfMyObject
are changed when you update the object stored in either variable.
When the values String()
, Number()
, and Boolean()
are created using the new keyword, or converted to complex objects behind the scenes, the values continue to be stored/copied by value. So, even though primitive values can be treated like complex values, they do not take on the quality of being copied by reference.
To truly make a copy of an object, you have to extract the values from the old object and inject them into a new object.
Complex Objects Are Equal by Reference
When comparing complex objects, they are equal only when they reference the same object (i.e. have the same address). Two variables containing identical objects are not equal to each other since they do not actually point at the same object.
In the following sample, objectFoo
and objectBar
have the same properties and are, in fact, identical objects, but when asked if they are equal via ===
, JavaScript tells us they are not.
Sample: sample19.html
<!DOCTYPE html><html lang="en"><body><script> var objectFoo = { same: 'same' }; var objectBar = { same: 'same' }; // Logs false, JS does not care that they are identical and of the same object type. console.log(objectFoo === objectBar); // How complex objects are measured for equality. var objectA = { foo: 'bar' }; var objectB = objectA; console.log(objectA === objectB); // Logs true because they reference the same object. </script></body></html>
The concept to take away here is that variables that point to a complex object in memory are equal only because they are using the same "address.” Conversely, two independently created objects are not equal even if they are of the same type and possess the exact same properties.
Complex Objects Have Dynamic Properties
A new variable that points to an existing complex object does not copy the object. This is why complex objects are sometimes called reference objects. A complex object can have as many references as you want, and they will always refer to the same object, even as the object being referenced changes.
Sample: sample20.html
<!DOCTYPE html><html lang="en"><body><script> var objA = { property: 'value' }; var pointer1 = objA; var pointer2 = pointer1; // Update the objA.property, and all references (pointer1 and pointer2) are updated. objA.property = null; // Logs 'null null null' because objA, pointer1, and pointer2 all reference the same object. console.log(objA.property, pointer1.property, pointer2.property); </script></body></html>
This allows for dynamic object properties because you can define an object, create references, update the object, and all of the variables referring to the object will "get" that update.
The typeof
Operator Used On Primitive and Complex Values
The typeof
operator can be used to return the type of value you are dealing with. But the values returned from it are not exactly consistent or what some might say, logical. The following code exhibits the returned values from using the typeof
operator.
Sample: sample21.html
<!DOCTYPE html><html lang="en"><body><script> // Primitive values. var myNull = null; var myUndefined = undefined; var primitiveString1 = "string"; var primitiveString2 = String('string'); var primitiveNumber1 = 10; var primitiveNumber2 = Number('10'); var primitiveBoolean1 = true; var primitiveBoolean2 = Boolean('true'); console.log(typeof myNull); // Logs object? WHAT? Be aware... console.log(typeof myUndefined); // Logs undefined. console.log(typeof primitiveString1, typeof primitiveString2); // Logs string string. console.log(typeof primitiveNumber1, typeof primitiveNumber2); // Logs number number console.log(typeof primitiveBoolean1, typeof primitiveBoolean2); // Logs boolean boolean. // Complex values. var myNumber = new Number(23); var myString = new String('male'); var myBoolean = new Boolean(false); var myObject = new Object(); var myArray = new Array('foo', 'bar'); var myFunction = new Function("x", "y", "return x * y"); var myDate = new Date(); var myRegExp = new RegExp('\\bt[a-z]+\\b'); var myError = new Error('Darn!'); console.log(typeof myNumber); // Logs object. console.log(typeof myString); // Logs object. console.log(typeof myBoolean); // Logs object. console.log(typeof myObject); // Logs object. console.log(typeof myArray); // Logs object. console.log(typeof myFunction); // Logs function? WHAT? Be aware... console.log(typeof myDate); // Logs object. console.log(typeof myRegExp); // Logs function? WHAT? Be aware... console.log(typeof myError); // Logs object. </script></body></html>
When using this operator on values, you should be aware of the potential values returned given the type of value (primitive or complex) that you are dealing with.
Dynamic Properties Allow for Mutable Objects
Complex objects are made up of dynamic properties. This allows user-defined objects, and most of the native objects, to be mutated. This means that the majority of objects in JavaScript can be updated or changed at any time. Because of this, we can change the native pre-configured nature of JavaScript itself by augmenting its native objects. However, I am not telling you to do this; in fact I do not think you should. But let's not cloud what is possible with opinions.
This means its possible to store properties on native constructors and add new methods to the native objects with additions to their prototype objects.
In the following code, I mutate the String()
constructor function and String.prototype
.
Sample: sample22.html
<!DOCTYPE html><html lang="en"><body><script> // Augment the built-in String constructor Function() with the augmentedProperties property. String.augmentedProperties = []; if (!String.prototype.trimIT) { // If the prototype does not have trimIT() add it. String.prototype.trimIT = function () { return this.replace(/^\s+|\s+$/g, ''); } // Now add trimIT string to the augmentedProperties array. String.augmentedProperties.push('trimIT'); } var myString = ' trim me '; console.log(myString.trimIT()); // Invoke our custom trimIT string method, logs 'trim me'. console.log(String.augmentedProperties.join()); // Logs 'trimIT'. </script></body></html>
I want to drive home the fact that objects in JavaScript are dynamic. This allows objects in JavaScript to be mutated. Essentially, the entire language can be mutated into a custom version (e.g., trimIT
string method). Again, I am not recommending thisI am just pointing out that it is part of the nature of objects in JavaScript.
Careful! If you mutate the native inner workings of JavaScript, you potentially have a custom version of JavaScript to deal with. Proceed with caution, as most people will assume that JavaScript is the same wherever its available.
All Constructor Instances Have Constructor Properties That Point to Their Constructor Function
When any object is instantiated, the constructor
property is created behind the scenes as a property of that object or instance. This property points to the constructor function that created the object. In the next code sample, we create an Object()
object, stored in the foo
variable, and then verify that the constructor
property is available for the object we created.
Sample: sample23.html
<!DOCTYPE html><html lang="en"><body><script> var foo = {}; console.log(foo.constructor === Object) // Logs true, because object() constructed foo. console.log(foo.constructor) // Points to the Object() constructor function. </script></body></html>
This can be useful: If Im working with some instance and I cant see who or what created it (especially if it is someone elses code), I can determine if its an array, an object, or whatever.
In the following sample, you can see that I have instantiated most of the pre-configured objects that come included with the JavaScript language. Note that using literal or primitive values does not mean that the constructor
pointer is not resolved when the primitive literal value is treated as an object.
Sample: sample24.html
<!DOCTYPE html><html lang="en"><body><script> var myNumber = new Number('23'); var myNumberL = 23; // Literal shorthand. var myString = new String('male'); var myStringL = 'male'; // Literal shorthand. var myBoolean = new Boolean('true'); var myBooleanL = true; // Literal shorthand. var myObject = new Object(); var myObjectL = {}; // Literal shorthand. var myArray = new Array(); var myArrayL = []; // Literal shorthand. var myFunction = new Function(); var myFunctionL = function () { }; // Literal shorthand. var myDate = new Date(); var myRegExp = new RegExp('/./'); var myRegExpL = /./; // Literal shorthand. var myError = new Error(); console.log( // All of these return true. myNumber.constructor === Number, myNumberL.constructor === Number, myString.constructor === String, myStringL.constructor === String, myBoolean.constructor === Boolean, myBooleanL.constructor === Boolean, myObject.constructor === Object, myObjectL.constructor === Object, myArray.constructor === Array, myArrayL.constructor === Array, myFunction.constructor === Function, myFunctionL.constructor === Function, myDate.constructor === Date, myRegExp.constructor === RegExp, myRegExpL.constructor === RegExp, myError.constructor === Error ); </script></body></html>
The constructor
property also works on user-defined constructor functions. In the following sample, we define a CustomConstructor()
constructor function, then using the keyword new
, we invoke the function to produce an object. Once we have our object, we can then leverage the constructor
property.
Sample: sample25.html
<!DOCTYPE html><html lang="en"><body><script> var CustomConstructor = function CustomConstructor() { return 'Wow!'; }; var instanceOfCustomObject = new CustomConstructor(); // Logs true. console.log(instanceOfCustomObject.constructor === CustomConstructor); // Returns a reference to CustomConstructor() function. // Returns 'function() { return 'Wow!'; };' console.log(instanceOfCustomObject.constructor); </script></body></html>
You might be confused as to why primitive values have constructor properties that point to constructor functions when objects are not returned. By using a primitive value, the constructor is still called, so there is still a relationship with primitive values and constructor functions. However, the end result is a primitive value.
If you would like the constructor
property to log the actual name of the constructor for user-defined constructor functions, you have to give the constructor function an actual name (e.g., var Person = function Person(){};
).
Verify That an Object Is an Instance of a Particular Constructor Function
By using the instanceof
operator, we can determine (true or false) if an object is an instance of a particular constructor function.
In the next sample, we are verifying if the object InstanceOfCustomObject
is an instance of the CustomConstructor
constructor function. This works with user-defined objects as well as native objects created with the new
operator.
Sample: sample26.html
<!DOCTYPE html><html lang="en"><body><script> // User-defined object constructor. var CustomConstructor = function () { this.foo = 'bar'; }; // Instantiate an instance of CustomConstructor. var instanceOfCustomObject = new CustomConstructor(); console.log(instanceOfCustomObject instanceof CustomConstructor); // Logs true. // Works the same as a native object. console.log(new Array('foo') instanceof Array) // Logs true. </script></body></html>
One thing to watch out for when dealing with the instanceof
operator is that it will return true
any time you ask if an object is an instance of Object
, since all objects inherit from the Object()
constructor.
The instanceof
operator will return false when dealing with primitive values that leverage object wrappers (e.g., 'foo' instanceof String // returns false
). Had the string 'foo'
been created with the new
operator, the instanceof
operator would have returned true. So, keep in mind that instanceof
really only works with complex objects and instances created from constructor functions that return objects.
An Instance Created From a Constructor Can Have Its Own Independent Properties (Aka Instance Properties)
In JavaScript, objects can be augmented at any time (i.e. dynamic properties). As previously mentioned, and to be exact, JavaScript has mutable objects. This means that objects created from a constructor function can be augmented with properties.
In the following code sample, I create an instance from the Array()
constructor and then augment it with its own property.
Sample: sample27.html
<!DOCTYPE html><html lang="en"><body><script> var myArray = new Array(); myArray.prop = 'test'; console.log(myArray.prop) // Logs 'test'. </script></body></html>
This could be done with Object()
, RegExp()
, or any of the other non-primitive constructors—even Boolean()
.
Sample: sample28.html
<!DOCTYPE html><html lang="en"><body><script> // This can be done with any of the native constructors that actually produce an object. var myString = new String(); var myNumber = new Number(); var myBoolean = new Boolean(true); var myObject = new Object(); var myArray = new Array(); var myFunction = new Function('return 2+2'); var myRegExp = new RegExp('\bt[a-z]+\b'); myString.prop = 'test'; myNumber.prop = 'test'; myBoolean.prop = 'test'; myObject.prop = 'test'; myArray.prop = 'test'; myFunction.prop = 'test'; myRegExp.prop = 'test'; // Logs 'test', 'test', 'test', 'test', 'test', 'test', 'test'. console.log(myString.prop, myNumber.prop, myBoolean.prop, myObject.prop, myArray.prop, myFunction.prop, myRegExp.prop); // Be aware: Instance properties do not work with primitive/literal values. var myString = 'string'; var myNumber = 1; var myBoolean = true; myString.prop = true; myNumber.prop = true; myBoolean.prop = true; // Logs undefined, undefined, undefined. console.log(myString.prop, myNumber.prop, myBoolean.prop); </script></body></html>
Adding properties to objects created from a constructor function sometimes occurs. Remember, object instances created from constructor functions are just plain old objects.
Keep in mind that besides their own properties, instances can have properties inherited from the prototype chain. Or, as we just saw in the previous code sample, properties added to the constructor after instantiation. This highlights the dynamic nature of objects in JavaScript.
The Semantics of "JavaScript Objects" and "Object()
Objects"
Do not confuse the general term "JavaScript objects," which refers to the notion of objects in JavaScript, with Object()
objects. An Object()
object (e.g., var myObject = new Object()
) is a very specific type of value expressed in JavaScript. Just as an Array()
object is a type of object called array, an Object()
object is a type of object called object. The gist is that the Object()
constructor function produces an empty generic object container, which is referred to as an Object()
object. Similarly, the Array()
constructor function produces an array object, and we refer to these objects as Array()
objects.
In this book, the term "JavaScript objects" is used to refer to all objects in JavaScript, because most of the values in JavaScript can act like objects. This is due to the fact that the majority of JavaScript values are created from a native constructor function which produces a very specific type of object.
Conclusion
What you need to remember is that an Object()
object is a very specific kind of value. Its a generic empty object. Do not confuse this with the term "JavaScript objects" used to refer to most of the values that can be expressed in JavaScript as an object.
Comments