Chapter 4. Objects

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

JavaScript has an eccentric take on the classical Object-oriented programming. Object-oriented programming is one of the most popular programming paradigms and has been a mainstay in most of programming languages like Java and C++. There are well defined ideas proposed by classical OOP that most of these languages adopt. JavaScript, however, has a different take on it. We will look JavaScript's way of supporting OOP.

In this chapter, you will learn the following topics:

  • 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. Consider the following example:

    > 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 the difference is 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 keys 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. Some of the cases are stated here:

  • 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 {}. However, first, let's introduce this bit of terminology - defining an array with [] is called array literal notation, and defining an object using 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, probably 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 the 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 the 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 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' 
    }; 

Following is an example for accessing a property with the dot notation:

    > hero.breed; 
    "Turtle" 

Let's see an example for accessing a property with the bracket notation:

    > hero['occupation']; 
    "Ninja" 

Consider the following example for 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 can use the following lines of code:

    > book.author.firstname; 
    "Joseph" 

Let see an example 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 in the same way in which 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 would proceed as you would with normal functions:

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

As you can use the array-like square brackets to access a property, it 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 it is instead defined at runtime:

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

Note

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 = {}; 

Note

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.

  1. Following is the code to access an non-existing property:
            > typeof hero.breed; 
            "undefined" 
    
  2. Adding two properties and a method:
            > hero.breed = 'turtle'; 
            > hero.name = 'Leonardo'; 
            > hero.sayName = function () { 
                return hero.name;  
              }; 
    
  3. Calling the method:
            > hero.sayName(); 
            "Leonardo" 
    
  4. Deleting a property:
            > delete hero.name; 
            true 
    
  5. If you call the method again, it will no longer find the deleted name property:
            > hero.sayName(); 
            "undefined" 
    

Note

Malleable objects

You can always change any object at any time, such as adding and removing properties and changing their values. However, 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. This method is 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-using constructor functions. Let's look at an example:

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

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

    > 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" 

Note

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

If you call a function that is designed to be a constructor but you omit the new operator, it is not an error. However, 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 the variable h contains the value that the function returns. The function does not return anything (there's no return function), so it actually returns undefined, which gets assigned to the variable 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 keyword 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 as follows:

    > 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 

As you had this keyword inside the function 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 

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; 
    } 

As 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 whether 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 the new operator. 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 
      }; 
    } 

Consider the following example 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 keyword. 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 

However, 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 this object, 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. Consider the following code:

    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. If you compare two distinct objects that happen to have the exact same methods and properties, the result would be 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, the variable 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 

As 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. Consider the following example:

Objects in the WebKit console

Note

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

Logging using the console.log method

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.

Logging using the console.log method

The console.log() method is convenient when you want to quickly test something, as well as when you want to dump some intermediate debugging information in your real scripts. 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 

ES6 object literals

ES6 introduces a much succinct syntax while using object literals. ES6 offers several shorthands for property initialization and function definitions. ES6 shorthands closely resemble a familiar JSON syntax. Consider the following code fragment:

    let a = 1 
    let b = 2 
    let val = {a: a, b: b} 
    console.log(val) //{"a":1,"b":2} 

This is a typical way to assign property values. If the name of the variable and the property key is the same, ES6 allows you to use shorthand syntax. The preceding code can be written as follows:

    let a = 1 
    let b = 2 
    let val = {a, b} 
    console.log(val) //{"a":1,"b":2} 

Similar syntax is available for method definitions as well. As we have discussed, methods are simply properties of an object whose values are functions. Consider the following example:

    var obj = { 
      prop: 1, 
      modifier:  function() { 
        console.log(this.prop);   
      } 
    } 

There is a compact way to define methods in ES6. You simply drop the function keyword and :. The equivalent code in ES6 would look like the following:

    var obj = { 
      prop: 1, 
      modifier () { 
        console.log(this.prop); 
      } 
    } 

ES6 allows you to compute the key of a property. Until ES6, you could only use fixed property names. Here is an example:

    var obj = { 
      prop: 1, 
      modifier: function () { 
        console.log(this.prop);   
      } 
    } 
    obj.prop = 2; 
    obj.modifier(); //2 

As you can see, we are limited to using fixed key names: prop and modifier in this case. However, ES6 allows you to use computed property keys. It is possible to create property keys dynamically using values returned by a function as well:

    let vehicle = "car" 
    function vehicleType(){ 
      return "truck" 
    } 
    let car = { 
      [vehicle+"_model"]: "Ford" 
    } 
    let truck= { 
      [vehicleType() + "_model"]: "Mercedez" 
    } 
    console.log(car) //{"car_model":"Ford"} 
    console.log(truck) //{"truck_model":"Mercedez"} 

We are using the value of variable vehicle to concatenate with a fixed string to derive the property key while creating the car object. In the second snippet, we are creating a property by concatenating a fixed string with the value returned by a function. This way of computing property keys provides great flexibility while creating objects, and a lot of boilerplate and repetitive code can be eliminated.

This syntax is applicable to method definition as well:

    let object_type = "Vehicle" 
    let obj = { 
      ["get"+object_type]() { 
        return "Ford" 
      } 
    } 
..................Content has been hidden....................

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