London

ANDREA

MARANGONI

  • HOME

  • ABOUT

  • PROJECTS

  • CONTACTS

    • gmail
    • github.com
    • linkedin
    • skype
    • stackoverflow
    • delicious
    • flickr
    • twitter

Javascript Notes

17 March 2014

Introduction

Here I want to collect main concepts extracted from the book 'Javascript: the Good Parts' by Douglas Crockford to have and to give to others, a quick reference. The book is really well written, simple and smooth, and offers plenty of great 'best practices'.

Object Literals

    var flight = {
        airline: "Oceanic",
        number: 815,
        departure: {
            IATA: "SYD",
            time: "2004-09-22 14:55",
            city: "Sydney"
        },
        arrival: {
            IATA: "LAX",
            time: "2004-09-23 10:42",
            city: "Los Angeles"
        }   
    };

Retrieve Objects Values

Two ways to retrieve values: using [] or using the ..

    flight["number"]
    flight.number

have the same result. We can generate a default value using ||.

    var status = flight.status || "unknown";

Attempting to retrive a value from a nonexixting property generate an undefined value, and attempting to retrive a value from undefined will generate a TypeError exception.

    flight.equipment                            //undefined
    flight.equipment.model                      //TypeError
    flight.equipment && flight.equipment.model  //to avoid exception..

Updating Objects Values

Using assignment we can update a property's value. If the property already exists the value will be replaced, if the property doesn't exist the object will be augmented.

    flight["number"] = 719;    // update
    flight.status = 'overdue'; // object augmented

References

Objects are passed around by reference. They are never copied. That's it.

Prototype

Every object is linked to a prototype object from which it can inherit properties: Object.prototype. When you make a new object, you can select the object that should be its prototype. Let's add a create() method to Object. This method creates a new object that uses an old object as its prototype.

    Object.prototype.create = function(o) {
        if (typeof Object.create !== 'function') {
            Object.create = function(o) {
                var F = function() {};
                F.prototype = o;
                return new F();
            };
        }
    };

    var another_flight = Object.create(flight);

The prototype link has no effect on updating. The prototype link is used only in retrieval. If we add a property to a prototype, that property will be visible to every object based on that prototype.

    flight.status = "cancelled";
    another_flight.status // cancelled;

Reflection

typeof operator can be very useful to know the type of a property:

    typeof flight.number   //'number'
    typeof flight.status   //'string'
    typeof flight.arrival  //'object'
    typeof flight.manifest //'undefined'

Must be careful with values like:

    typeof flight.toString //'function'
    typeof flight.constructor //'function'

Because every property on the chain can produce a value we have to deal with it. Two approaches to deal with undesired values:

  1. 1. check and reject 'function' values
  2. 2. use the hasOwnProperty method

Delete

We can delete a property with the delete keyword, without touching the prototype linkage. On the other hand we can reveal property of the prototype linkage.

Function Objects

Function are objects. Objects are collections of name/value pairs having a hidden link to a prototype object. Function objects are linked to Function.prototype which is itself linked to Object.prototype. Functions can be stored in variables, objects, and arrays. Functions can be passed as arguments to functions, and functions can be returned from functions. Also, since functions are objects, functions can have methods. Function can be invoked.

Function Literals

Function objects are created with function literals:

    // Create a variable called add and store a function
    // in it that adds two numbers.
    var add = function (a, b) {
        return a + b;
    };

A function literal can appear anywhere that an expression can appear. Functions can be defined inside of other functions. An inner function of course has access to its parameters and variables. An inner function also enjoys access to the parameters and variables of the functions it is nested within. The function object created by a function literal contains a link to that outer context (closure).

With parameters every function receive two extra parameters:

  1. • this
  2. • arguments

The this value is determined by the invocation pattern used. The patterns differ in how the bonus parameter this is initialized.

There are four different invocation patterns:

  1. 1. Method invocation pattern: a function is stored as a property of an object, we call it a method.

     //Create myObject. It has a value and an increment
     //method. The increment method takes an optional
     //parameter. If the argument is not a number, then 1
     //is used as the default.
     var myObject = {
         value: 0,
         increment: function (inc) {
             this.value += typeof inc === 'number' ? inc : 1;
         }
     };
     myObject.increment( );
     document.writeln(myObject.value); // 1
     myObject.increment(2);
     document.writeln(myObject.value); // 3
    

    The binding of this to the object happens at invocation time. This very late binding makes functions that use this highly reusable.

  2. 2. Function invocation pattern: When a function is not the property of an object, then it is invoked as a function:

     var sum = add(3, 4); // sum is 7
    

    When a function is invoked with this pattern, this is bound to the global object. This was a mistake in the design of the language. Workaround: If the method defines a variable and assigns it the value of this , the inner function will have access to this through that variable. By convention, the name of that variable is that :

     // Augment myObject with a double method.
     myObject.double = function ( ) {
         var that = this; // Workaround.
         var helper = function ( ) {
             that.value = add(that.value, that.value);
         };
         helper(); // Invoke helper as a function.
     };
     // Invoke double as a method.
     myObject.double( );
     document.writeln(myObject.getValue( )); // 6
    
  3. 3. Constructor invocation pattern: If a function is invoked with the new prefix, then a new object will be created with a hidden link to the value of the function’s prototype member, and this will be bound to that new object. The new prefix also changes the behavior of the return statement: if the return value is not an object, then this (the new object) is returned instead.

     // Create a constructor function called Quo.
     // It makes an object with a status property.
     var Quo = function (string) {
         this.status = string;
     };
     // Give all instances of Quo a public method
     // called get_status.
     Quo.prototype.get_status = function ( ) {
         return this.status;
     };
     // Make an instance of Quo.
     var myQuo = new Quo("confused");
     document.writeln(myQuo.get_status( )); // confused
    

    Functions that are intended to be used with the new prefix are called constructors. If a constructor is called without the new prefix, very bad things can happen without a compile-time or runtime warning.
    Use of this style of constructor functions is not recommended.

  4. 4. Apply invocation pattern: The apply method lets us construct an array of arguments to use to invoke a function. It also lets us choose the value of this. The apply method takes two parameters. The first is the value that should be bound to this . The second is an array of parameters.

     // Make an array of 2 numbers and add them.
     var array = [3, 4];
     var sum = add.apply(null, array); // sum is 7
     // Make an object with a status member.
     var statusObject = {
         status: 'A-OK'
     };
     //statusObject does not inherit from Quo.prototype,
     //but we can invoke the get_status method on
     //statusObject even though statusObject does not have
     //a getStatus method.
     var status = Quo.prototype.getStatus.apply(statusObject); 
     //status is 'A-OK'
    

Augmenting Types

We can make a method available to all functions simply augmenting Function.prototype.

    //We no longer have to type the name of the prototype property
    Function.prototype.method = function (name, func) {
        this.prototype[name] = func;
        return this;
    };

    //Extract the integer part of a number
    Number.method('integer', function ( ) {
        return Math[this < 0 ? 'ceiling' : 'floor'](this);
    });
    document.writeln((-10 / 3).integer( )); // -3

    String.method('trim', function ( ) {
        return this.replace(/^\s+|\s+$/g, '');
    });
    document.writeln('"' + "  neat  ".trim( ) + '"'); 

Functions scope

JavaScript does not have block scope even though its block syntax suggests that it does. This confusion can be a source of errors. It is best to declare all of the variables used in a function at the top of the function body. JavaScript does have function scope.

Closure

The good news about scope is that inner functions get access to the parameters and variables of the functions they are defined within. Exception made for this and arguments.
We previously made a myObject that had a value and an increment method. Suppose we wanted to protect the value from unauthorized changes. Instead of initializing myObject with an object literal, we will initialize myObject by calling a function that returns an object literal. That function defines a value variable. That variable is always available to the increment and getValue methods, but the function’s scope keeps it hidden from the rest of the program:

    var myObject = function ( ) {
        var value = 0;
        return {
                increment: function (inc) {
                    value += typeof inc === 'number' ? inc : 1;
            },
            getValue: function ( ) {
                return value;
            }
        };
    }();

We are not assigning a function to myObject. We are assigning the result of invoking that function. Notice the () on the last line. The function returns an object containing two methods, and those methods continue to enjoy the privilege of access to the value variable.

Let’s define a function to be used without the new prefix:

    // Create a maker function called quo. It makes an
    // object with a get_status method and a private
    // status property.
    var quo = function (status) {
        return {
            get_status: function ( ) {
                return status;
            }
        };
    };
    // Make an instance of quo.
    var myQuo = quo("amazed");
    document.writeln(myQuo.get_status( ));

When we call quo , it returns a new object containing a get_status method. get_status does not have access to a copy of the parameter; it has access to the parameter itself. This is possible because the function has access to the context in which it was created. This is called closure.

Modules

A module is a function or object that presents an interface but that hides its state and implementation. By using functions to produce modules, we can almost completely eliminate our use of global variables, thereby mitigating one of JavaScript’s worst features. For example, suppose we want to augment String with a deentityify method. Its job is to look for HTML entities in a string and replace them with their equivalents. The ideal approach is to put it in a closure, and perhaps provide an extra method that can add additional entities:

    String.method('deentityify', function ( ) {
        // The entity table. It maps entity names to
        // characters.
        var entity = {
            quot: '"',
            lt: '<',
            gt: '>'
        };
        // Return the deentityify method.

        return function ( ) {

        //This is the deentityify method. It calls the string
        //replace method, looking for substrings that start
        //with '&' and end with ';'. If the characters in
        //between are in the entity table, then replace the
        //entity with the character from the table. It uses
        //a regular expression (Chapter 7).

            return this.replace(/&([^&;]+);/g, function (a, b) {
                var r = entity[b];
                return typeof r === 'string' ? r : a;
            });
        };
    }());

The general pattern of a module is a function that defines private variables and functions; creates privileged functions which, through closure, will have access to the private variables and functions; and that returns the privileged functions or stores them in an accessible place. It promotes information hiding and other good design practices.

Inheritance

JavaScript is a prototypal language, which means that objects inherit directly from other objects.

* Prototypal

You start by making a useful object. You can then make many more objects that are like that one.

    var myMammal = {
        name : 'Herb the Mammal',
        get_name : function ( ) {
            return this.name;
        },
        says : function ( ) {
            return this.saying || '';
        }
    };

Once we have an object that we like, we can make more instances with the Object.create method. We can then customize the new instances:

    var myCat = Object.create(myMammal);
    myCat.name = 'Henrietta';
    myCat.saying = 'meow';
    myCat.purr = function (n) {
            var i, s = '';
            for (i = 0; i < n; i += 1) {
                if (s) {
                s += '-';
            }
            s += 'r';
        }
        return s;
    };

    myCat.get_name = function ( ) {
        return this.says( ) + ' ' + this.name + ' ' + this.says( );
    };

This is differential inheritance. The problem is there is no privacy. Everything is public.

* Functional approach

We start by making a function that will produce objects.
The function contains four steps:

  1. 1. It creates a new object.
  2. 2. It optionally defines private instance variables and methods. These are just ordinary vars of the function.
  3. 3. It augments that new object with methods. Those methods will have privileged access to the parameters and the vars defined in the second step.
  4. 4. It returns that new object.

    var constructor = function (spec, my) {
        var that, other private instance variables;
        my = my || {};
        //Add shared variables and functions to my
        that = a new object;
        //Add privileged methods to that
        return that;
    };
    

The spec object contains all of the information that the constructor needs to make an instance. The contents of the spec could be copied into private variables or transformed by other functions. Or the methods can access information from spec as they need it. (A simplification is to replace spec with a single value. This is useful when the object being constructed does not need a whole spec object.) The my object is a container of secrets that are shared by the constructors in the inheritance chain. The use of the my object is optional. If a my object is not passed in, then a my object is made.
Declare the private instance variables and private methods for the object. The variables and inner functions of the constructor become the private members of the instance. The inner functions have access to spec and my and that and the private variables.
Add the shared secrets to the my object. This is done by assignment:

    my.member = value;

It's time to make a new object and assign it to that. How?

  • • We can use an object literal.
  • • We can use the Object.create method on a prototype object.
  • • We can call another functional constructor, passing it a spec object (possibly the same spec object that was passed to this constructor) and the my object.

We augment that, adding the privileged methods that make up the object’s interface. We can assign new functions to members of that. Or, more securely, we can define the functions first as private methods, and then assign them to that:

    var methodical = function ( ) {
    ...
    };
    that.methodical = methodical;

The advantage to defining methodical in two steps is that if other methods want to call methodical , they can call methodical() instead of that.methodical().
Finally we can return that.


© Andrea Marangoni 2013 - All Rights Reserved.