Wednesday, July 25, 2007

instance __proto__ chain of javascript


The term instance has a specific technical meaning in class-based languages. In these languages, an instance is an individual member of a class and is fundamentally different from a class. In JavaScript, "instance" does not have this technical meaning because JavaScript does not have this difference between classes and instances. However, in talking about JavaScript, "instance" can be used informally to mean an object created using a particular constructor function.
Object Properties
This section discusses how objects inherit properties from other objects in the prototype chain and what happens when you add a property at run time.
Inheriting Properties
Suppose you create the mark object as a WorkerBee (as shown in image) with the following statement:
mark = new WorkerBee;



When JavaScript sees the new operator, it creates a new generic object and passes this new object as the value of the this keyword to the WorkerBee constructor function. The constructor function explicitly sets the value of the projects property, and implicitly sets the value of the internal __proto__ property to the value of WorkerBee.prototype. (That property name has two underscore characters at the front and two at the end.) The __proto__ property determines the prototype chain used to return property values. Once these properties are set, JavaScript returns the new object and the assignment statement sets the variable mark to that object. This process does not explicitly put values in the mark object (local values) for the properties that mark inherits from the prototype chain. When you ask for the value of a property, JavaScript first checks to see if the value exists in that object. If it does, that value is returned. If the value is not there locally, JavaScript checks the prototype chain (using the __proto__ property). If an object in the prototype chain has a value for the property, that value is returned. If no such property is found, JavaScript says the object does not have the property. In this way, the mark object has the following properties and values:
mark.name = "";
mark.dept = "general";
mark.projects = [];
The mark object inherits values for the name and dept properties from the prototypical object in mark.__proto__. It is assigned a local value for the projects property by the WorkerBee constructor. This gives you inheritance of properties and their values in JavaScript. Some subtleties of this process are discussed in Property Inheritance Revisited. Because these constructors do not let you supply instance-specific values, this information is generic. The property values are the default ones shared by all new objects created from WorkerBee. You can, of course, change the values of any of these properties. So, you could give specific information for mark as follows:
mark.name = "Doe, Mark";
mark.dept = "admin";
mark.projects = ["navigator"];


Adding Properties
In JavaScript, you can add properties to any object at run time. You are not constrained to use only the properties provided by the constructor function. To add a property that is specific to a single object, you assign a value to the object, as follows:
mark.bonus = 3000;
Now, the mark object has a bonus property, but no other WorkerBee has this property.
If you add a new property to an object that is being used as the prototype for a constructor function, you add that property to all objects that inherit properties from the prototype. For example, you can add a specialty property to all employees with the following statement:
Employee.prototype.specialty = "none";
As soon as JavaScript executes this statement, the mark object also has the specialty property with the value of "none". The following figure shows the effect of adding this property to the Employee prototype and then overriding it for the Engineer prototype.


Determining Instance Relationships
You may want to know what objects are in the prototype chain for an object, so that you can tell from what objects this object inherits properties.
Starting with JavaScript 1.4, JavaScript provides an instanceof operator to test the prototype chain. This operator works exactly like the instanceof function discussed below. As discussed in Inheriting Properties, when you use the new operator with a constructor function to create a new object, JavaScript sets the __proto__ property of the new object to the value of the prototype property of the constructor function. You can use this to test the prototype chain. For example, suppose you have the same set of definitions already shown, with the prototypes set appropriately. Create a __proto__ object as follows:
chris = new Engineer("Pigman, Chris", ["jsd"], "fiji");
With this object, the following statements are all true:
chris.__proto__ == Engineer.prototype;
chris.__proto__.__proto__ == WorkerBee.prototype;
chris.__proto__.__proto__.__proto__ == Employee.prototype;
chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;
Given this, you could write an instanceOf function as follows:
function instanceOf(object, constructor) {
while (object != null) {
if (object == constructor.prototype)
return true;
object = object.__proto__;
}
return false;
}
With this definition, the following expressions are all true:
instanceOf (chris, Engineer)
instanceOf (chris, WorkerBee)
instanceOf (chris, Employee)
instanceOf (chris, Object)
But the following expression is false:
instanceOf (chris, SalesPerson)


Global Information in Constructors
When you create constructors, you need to be careful if you set global information in the constructor. For example, assume that you want a unique ID to be automatically assigned to each new employee. You could use the following definition for Employee:
var idCounter = 1;function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.id = idCounter++;
}
With this definition, when you create a new Employee, the constructor assigns it the next ID in sequence and then increments the global ID counter. So, if your next statement is the following, victoria.id is 1 and harry.id is 2:
victoria = new Employee("Pigbert, Victoria", "pubs")
harry = new Employee("Tschopik, Harry", "sales")
At first glance that seems fine. However, idCounter gets incremented every time an Employee object is created, for whatever purpose. If you create the entire Employee hierarchy shown in this chapter, the Employee constructor is called every time you set up a prototype. Suppose you have the following code:
var idCounter = 1;
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.id = idCounter++;
}
function Manager (name, dept, reports) {...}
Manager.prototype = new Employee;
function WorkerBee (name, dept, projs) {...}
WorkerBee.prototype = new Employee;
function Engineer (name, projs, mach) {...}
Engineer.prototype = new WorkerBee;
function SalesPerson (name, projs, quota) {...}
SalesPerson.prototype = new WorkerBee;
mac = new Engineer("Wood, Mac");
Further assume that the definitions omitted here have the base property and call the constructor above them in the prototype chain. In this case, by the time the mac object is created, mac.id is 5. Depending on the application, it may or may not matter that the counter has been incremented these extra times. If you care about the exact value of this counter, one possible solution involves instead using the following constructor:
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
if (name)
this.id = idCounter++;
}
When you create an instance of Employee to use as a prototype, you do not supply arguments to the constructor. Using this definition of the constructor, when you do not supply arguments, the constructor does not assign a value to the id and does not update the counter. Therefore, for an Employee to get an assigned id, you must specify a name for the employee. In this example, mac.id would be 1.


function doc(arg){
document.write(arg);
document.write('
');
}

function Shape() {
this.name = 'shape';
this.borderWidth = 5;
}

function Square() {
this.edge = 12;
}

Square.prototype = new Shape;

myPicture = new Square;

doc($H(Square.prototype).inspect().escapeHTML());
doc($H(myPicture.__proto__).inspect().escapeHTML());

doc(myPicture.constructor);
doc(myPicture.__proto__.constructor);
doc(myPicture.__proto__.__proto__.constructor);
doc(myPicture.__proto__.__proto__.__proto__.constructor);

doc(myPicture.borderWidth);

No comments :