Chapter 4. Objects

Now that you've mastered JavaScript's primitive data types, arrays, and functions, it's time to make true to the promise of the book title and talk about objects.

In this chapter, you will learn:

  • How to create and use objects
  • What are the constructor functions
  • What types of built-in JavaScript objects exist and what they can do for you

From arrays to objects

As you already know from Chapter 2, Primitive Data Types, Arrays, Loops, and Conditions, an array is just a list of values. Each value has an index (a numeric key) that starts from zero and increments by one for each value.

> var myarr = ['red', 'blue', 'yellow', 'purple'];
> myarr;
 ["red", "blue", "yellow", "purple"].
> myarr[0];
"red"
> myarr[3];
"purple"

If you put the indexes in one column and the values in another, you'll end up with a table of key/value pairs shown as follows:

Key

Value

0

red

1

blue

2

yellow

3

purple

An object is similar to an array, but with the difference that you define the keys yourself. You're not limited to using only numeric indexes and you can use friendlier keys, such as first_name, age, and so on.

Let's take a look at a simple object and examine its parts:

var hero = {
  breed: 'Turtle', 
  occupation: 'Ninja'
};

You can see that:

  • The name of the variable that refers to the object is hero
  • Instead of [ and ], which you use to define an array, you use { and } for objects
  • You separate the elements (called properties) contained in the object with commas
  • The key/value pairs are divided by colons, as in key: value

The keys (names of the properties) can optionally be placed in quotation marks. For example, these are all the same:

var hero = {occupation: 1};
var hero = {"occupation": 1};
var hero = {'occupation': 1};

It's recommended that you don't quote the names of the properties (it's less typing), but there are cases when you must use quotes:

  • If the property name is one of the reserved words in JavaScript (see Appendix A, Reserved Words)
  • If it contains spaces or special characters (anything other than letters, numbers, and the _ and $ characters)
  • If it starts with a number

In other words, if the name you have chosen for a property is not a valid name for a variable in JavaScript, then you need to wrap it in quotes.

Have a look at this bizarre-looking object:

var o = {
  $omething: 1,
  'yes or no': 'yes',
  '!@#$%^&*': true
};

This is a valid object. The quotes are required for the second and the third properties, otherwise you'll get an error.

Later in this chapter, you'll see other ways to define objects and arrays in addition to [] and {}. But first, let's introduce this bit of terminology: defining an array with [] is called array literal notation, and defining an object using the curly braces {} is called object literal notation.

Elements, properties, methods, and members

When talking about arrays, you say that they contain elements. When talking about objects, you say that they contain properties. There isn't any significant difference in JavaScript; it's just the terminology that people are used to, likely from other programming languages.

A property of an object can point to a function, because functions are just data. Properties that point to functions are also called methods. In the following example, talk is a method:

var dog = {
  name: 'Benji',
  talk: function () {
    alert('Woof, woof!'),
  }
};

As you have seen in the previous chapter, it's also possible to store functions as array elements and invoke them, but you'll not see much code like this in practice:

> var a = [];
> a[0] = function (what) { alert(what); };
> a[0]('Boo!'),

You can also see people using the word members to refer to properties of an object, most often when it doesn't matter if the property is a function or not.

Hashes and associative arrays

In some programming languages, there is a distinction between:

  • A regular array, also called an indexed or enumerated array (the keys are numbers)
  • An associative array, also called a hash or a dictionary (the keys are strings)

JavaScript uses arrays to represent indexed arrays and objects to represent associative arrays. If you want a hash in JavaScript, you use an object.

Accessing an object's properties

There are two ways to access a property of an object:

  • Using the square bracket notation, for example hero['occupation']
  • Using the dot notation, for example hero.occupation

The dot notation is easier to read and write, but it cannot always be used. The same rules apply as for quoting property names: if the name of the property is not a valid variable name, you cannot use the dot notation.

Let's take the hero object again:

var hero = {
  breed: 'Turtle',
  occupation: 'Ninja'
};

Accessing a property with the dot notation:

> hero.breed;
"Turtle"

Accessing a property with the bracket notation:

> hero['occupation'];
"Ninja"

Accessing a non-existing property returns undefined:

> 'Hair color is ' + hero.hair_color;
"Hair color is undefined"

Objects can contain any data, including other objects:

var book = {
  name: 'Catch-22',
  published: 1961,
  author: {
    firstname: 'Joseph',
    lastname: 'Heller'
  }
};

To get to the firstname property of the object contained in the author property of the book object, you use:

> book.author.firstname;
"Joseph"

Using the square brackets notation:

> book['author']['lastname'];
"Heller"

It works even if you mix both:

> book.author['lastname'];
"Heller"
> book['author'].lastname;
"Heller"

Another case where you need square brackets is when the name of the property you need to access is not known beforehand. During runtime, it's dynamically stored in a variable:

> var key = 'firstname';
> book.author[key];
"Joseph"

Calling an object's methods

You know a method is just a property that happens to be a function, so you access methods the same way as you would access properties: using the dot notation or using square brackets. Calling (invoking) a method is the same as calling any other function: you just add parentheses after the method name, which effectively says "Execute!".

> var hero = {
    breed: 'Turtle',
    occupation: 'Ninja',
    say: function () {
      return 'I am ' + hero.occupation;
    }
  };
> hero.say();
"I am Ninja"

If there are any parameters that you want to pass to a method, you proceed as with normal functions:

> hero.say('a', 'b', 'c'),

Because you can use the array-like square brackets to access a property, this means you can also use brackets to access and invoke methods:

> hero['say']();

This is not a common practice unless the method name is not known at the time of writing code, but is instead defined at runtime:

var method = 'say';
hero[method]();

Tip

Best practice tip: no quotes (unless you have to)

Use the dot notation to access methods and properties and don't quote properties in your object literals.

Altering properties/methods

JavaScript allows you to alter the properties and methods of existing objects at any time. This includes adding new properties or deleting them. You can start with a "blank" object and add properties later. Let's see how you can go about doing this.

An object without properties is shown as follows:

> var hero = {};

Tip

A "blank" object

In this section, you started with a "blank" object, var hero = {};. Blank is in quotes because this object is not really empty and useless. Although at this stage it has no properties of its own, it has already inherited some. You'll learn more about own versus inherited properties later. So, an object in ES3 is never really "blank" or "empty". In ES5 though, there is a way to create a completely blank object that doesn't inherit anything, but let's not get ahead too much.

Accessing a non-existing property is shown as follows:

> typeof hero.breed;
"undefined"

Adding two properties and a method:

> hero.breed = 'turtle';
> hero.name = 'Leonardo';
> hero.sayName = function () {
    return hero.name; 
  };

Calling the method:

> hero.sayName();
"Leonardo"

Deleting a property:

> delete hero.name;
true

Calling the method again will no longer find the deleted name property:

> hero.sayName();
"undefined"

Tip

Malleable objects

You can always change any object at any time, such as adding and removing properties and changing their values. But, there are exceptions to this rule. A few properties of some built-in objects are not changeable (for example, Math.PI, as you'll see later). Also, ES5 allows you to prevent changes to objects; you'll learn more about it in Appendix C, Built-in Objects.

Using the this value

In the previous example, the sayName() method used hero.name to access the name property of the hero object. When you're inside a method though, there is another way to access the object the method belongs to: by using the special value this.

> var hero = {
    name: 'Rafaelo',
    sayName: function () {
      return this.name;
    }
  };
> hero.sayName();
"Rafaelo"

So, when you say this, you're actually saying "this object" or "the current object".

Constructor functions

There is another way to create objects: by using constructor functions. Let's see an example:

function Hero() {
  this.occupation = 'Ninja';
}

In order to create an object using this function, you use the new operator, like this:

> var hero = new Hero();
> hero.occupation;
"Ninja"

A benefit of using constructor functions is that they accept parameters, which can be used when creating new objects. Let's modify the constructor to accept one parameter and assign it to the name property:

function Hero(name) {
  this.name = name;
  this.occupation = 'Ninja';
  this.whoAreYou = function () {
    return "I'm " +
           this.name +
           " and I'm a " +
           this.occupation;
  };
}

Now you can create different objects using the same constructor:

> var h1 = new Hero('Michelangelo'),
> var h2 = new Hero('Donatello'),
> h1.whoAreYou();
"I'm Michelangelo and I'm a Ninja"
> h2.whoAreYou();
"I'm Donatello and I'm a Ninja"

Tip

Best practice

By convention, you should capitalize the first letter of your constructor functions so that you have a visual clue that this is not intended to be called as a regular function.

If you call a function that is designed to be a constructor but you omit the new operator, this is not an error, but it doesn't give you the expected result.

> var h = Hero('Leonardo'),
> typeof h;
"undefined"

What happened here? There is no new operator, so a new object was not created. The function was called like any other function, so h contains the value that the function returns. The function does not return anything (there's no return), so it actually returns undefined, which gets assigned to h.

In this case, what does this refer to? It refers to the global object.

The global object

You have already learned a bit about global variables (and how you should avoid them). You also know that JavaScript programs run inside a host environment (the browser for example). Now that you know about objects, it's time for the whole truth: the host environment provides a global object and all global variables are accessible as properties of the global object.

If your host environment is the web browser, the global object is called window. Another way to access the global object (and this is also true in most other environments) is to use this outside a constructor function, for example in the global program code outside any function.

As an illustration, you can declare a global variable outside any function, such as:

> var a = 1;

Then, you can access this global variable in various ways:

  • As a variable a
  • As a property of the global object, for example window['a'] or window.a
  • As a property of the global object referred to as this:
    > var a = 1;
    > window.a;
    1
    > this.a;
    1
    

Let's go back to the case where you define a constructor function and call it without the new operator. In such cases, this refers to the global object and all the properties set to this become properties of window.

Declaring a constructor function and calling it without new returns "undefined":

> function Hero(name) {
    this.name = name;
  }
> var h = Hero('Leonardo'),
> typeof h;
"undefined"
> typeof h.name;
TypeError: Cannot read property 'name' of undefined

Because you had this inside Hero, a global variable (a property of the global object) called name was created:

> name;
"Leonardo"
> window.name;
"Leonardo"

If you call the same constructor function using new, then a new object is returned and this refers to it:

> var h2 = new Hero('Michelangelo'),
> typeof h2;
"object"
> h2.name;
"Michelangelo"

The built-in global functions you have seen in Chapter 3, Functions, can also be invoked as methods of the window object. So, the following two calls have the same result:

> parseInt('101 dalmatians'),
101
> window.parseInt('101 dalmatians');
101

And, when outside a function called as a constructor (with new), also:

> this.parseInt('101 dalmatians');
101

The constructor property

When an object is created, a special property is assigned to it behind the scenes—the constructor property. It contains a reference to the constructor function used to create this object.

Continuing from the previous example:

> h2.constructor;
function Hero(name) {
  this.name = name;
}

Because the constructor property contains a reference to a function, you might as well call this function to produce a new object. The following code is like saying, "I don't care how object h2 was created, but I want another one just like it":

> var h3 = new h2.constructor('Rafaello'),
> h3.name;
"Rafaello"

If an object was created using the object literal notation, its constructor is the built-in Object() constructor function (there is more about this later in this chapter):

> var o = {};
> o.constructor;
function Object() { [native code] }
> typeof o.constructor;
"function"

The instanceof operator

With the instanceof operator, you can test if an object was created with a specific constructor function:

> function Hero() {}
> var h = new Hero();
> var o = {};
> h instanceof Hero;
true
> h instanceof Object;
true
> o instanceof Object;
true

Note that you don't put parentheses after the function name (you don't use h instanceof Hero()). This is because you're not invoking this function, but just referring to it by name, as with any other variable.

Functions that return objects

In addition to using constructor functions and the new operator to create objects, you can also use a normal function to create objects without new. You can have a function that does a bit of preparatory work and has an object as a return value.

For example, here's a simple factory() function that produces objects:

function factory(name) {
  return {
    name: name
  };
}

Using the factory() function:

> var o = factory('one'),
> o.name;
"one"
> o.constructor;
function Object() { [native code] }

In fact, you can also use constructor functions and return objects different from this. This means you can modify the default behavior of the constructor function. Let's see how.

Here's the normal constructor scenario:

> function C() {
    this.a = 1;
  }
> var c = new C();
> c.a;
1

But now look at this scenario:

> function C2() {
    this.a = 1;
    return {b: 2};
  }
> var c2 = new C2();
> typeof c2.a;
"undefined"
> c2.b;
2

What happened here? Instead of returning the object this, which contains the property a, the constructor returned another object that contains the property b. This is possible only if the return value is an object. Otherwise, if you try to return anything that is not an object, the constructor will proceed with its usual behavior and return this.

If you think about how objects are created inside constructor functions, you can imagine that a variable called this is defined at the top of the function and then returned at the end. It's as if something like this happens:

function C() {
  // var this = {}; // pseudo code, you can't do this
  this.a = 1;
  // return this;
}

Passing objects

When you assign an object to a different variable or pass it to a function, you only pass a reference to that object. Consequently, if you make a change to the reference, you're actually modifying the original object.

Here's an example of how you can assign an object to another variable and then make a change to the copy. As a result, the original object is also changed:

> var original = {howmany: 1};
> var mycopy = original;
> mycopy.howmany;
1
> mycopy.howmany = 100;
100
> original.howmany;
100

The same thing applies when passing objects to functions:

> var original = {howmany: 100};
> var nullify = function (o) { o.howmany = 0; };
> nullify(original);
> original.howmany;
0

Comparing objects

When you compare objects, you'll get true only if you compare two references to the same object. Comparing two distinct objects that happen to have the exact same methods and properties returns false.

Let's create two objects that look the same:

> var fido  = {breed: 'dog'};
> var benji = {breed: 'dog'};

Comparing them returns false:

> benji === fido;
false
> benji == fido;
false

You can create a new variable, mydog, and assign one of the objects to it. This way, mydog actually points to the same object:

> var mydog = benji;

In this case, benji is mydog because they are the same object (changing the mydog variable's properties will change the benji variable's properties). The comparison returns true:

> mydog === benji;
true

And, because fido is a different object, it does not compare to mydog:

> mydog === fido;
false

Objects in the WebKit console

Before diving into the built-in objects in JavaScript, let's quickly say a few words about working with objects in the WebKit console.

After playing around with the examples in this chapter, you might have already noticed how objects are displayed in the console. If you create an object and type its name, you'll get an arrow pointing to the word Object.

The object is clickable and expands to show you a list of all of the properties of the object. If a property is also an object, there is an arrow next to it too, so you can expand this as well. This is handy as it gives you an insight into exactly what this object contains.

Objects in the WebKit console

You can ignore __proto__ for now; there's more about it in the next chapter.

console.log

The console also offers you an object called console and a few methods, such as console.log() and console.error(), which you can use to display any value you want in the console.

console.log

console.log() is convenient when you want to quickly test something, as well as in your real scripts when you want to dump some intermediate debugging information. Here's how you can experiment with loops for example:

> for (var i = 0; i < 5; i++) {
    console.log(i); 
  }
0
1
2
3
4
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.100.42