Core JavaScript 1.5 Guide:The Employee Example:More Flexible Constructors
From MDC
[edit] More Flexible Constructors
The constructor functions shown so far do not let you specify property values when you create an instance. As with Java, you can provide arguments to constructors to initialize property values for instances. The following figure shows one way to do this.

Figure 8.5: Specifying properties in a constructor, take 1
The following table shows the Java and JavaScript definitions for these objects.
| JavaScript | Java |
|---|---|
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
}
|
public class Employee {
public String name;
public String dept;
public Employee () {
this("", "general");
}
public Employee (String name) {
this(name, "general");
}
public Employee (String name, String dept) {
this.name = name;
this.dept = dept;
}
}
|
function WorkerBee (projs) {
this.projects = projs || [];
}
WorkerBee.prototype = new Employee;
|
public class WorkerBee extends Employee {
public String[] projects;
public WorkerBee () {
this(new String[0]);
}
public WorkerBee (String[] projs) {
this.projects = projs;
}
}
|
function Engineer (mach) {
this.dept = "engineering";
this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
|
public class Engineer extends WorkerBee {
public String machine;
public WorkerBee () {
this.dept = "engineering";
this.machine = "";
}
public WorkerBee (String mach) {
this.dept = "engineering";
this.machine = mach;
}
}
|
These JavaScript definitions use a special idiom for setting default values:
this.name = name || "";
The JavaScript logical OR operator (||) evaluates its first argument. If that argument converts to true, the operator returns it. Otherwise, the operator returns the value of the second argument. Therefore, this line of code tests to see if name has a useful value for the name property. If it does, it sets this.name to that value. Otherwise, it sets this.name to the empty string. This chapter uses this idiom for brevity; however, it can be puzzling at first glance. Please note: This may not work as expected with numeric or boolean arguments, as both 0 (zero) and false will cause the default value to be chosen; in this case you will need to use the following more cumbersome idiom, which gives the desired behaviour with all data types:
this.authorized = typeof(authorized) !== 'undefined' ? authorized : true;
With these definitions, when you create an instance of an object, you can specify values for the locally defined properties. As shown in Figure 8.5, you can use the following statement to create a new Engineer:
jane = new Engineer("belau");
Jane's properties are now:
jane.name == ""; jane.dept == "engineering"; jane.projects == []; jane.machine == "belau"
Notice that with these definitions, you cannot specify an initial value for an inherited property such as name. If you want to specify an initial value for inherited properties in JavaScript, you need to add more code to the constructor function.
So far, the constructor function has created a generic object and then specified local properties and values for the new object. You can have the constructor add more properties by directly calling the constructor function for an object higher in the prototype chain. The following figure shows these new definitions.

Figure 8.6 Specifying properties in a constructor, take 2
Let's look at one of these definitions in detail. Here's the new definition for the Engineer constructor:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
Suppose you create a new Engineer object as follows:
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
JavaScript follows these steps:
- The new operator creates a generic object and sets its
__proto__property toEngineer.prototype. - The new operator passes the new object to the
Engineerconstructor as the value of thethiskeyword. -
The constructor creates a new property called
basefor that object and assigns the value of theWorkerBeeconstructor to thebaseproperty. This makes theWorkerBeeconstructor a method of theEngineerobject.The name of the
baseproperty is not special. You can use any legal property name;baseis simply evocative of its purpose. - The constructor calls the
basemethod, passing as its arguments two of the arguments passed to the constructor ("Doe, Jane" and ["navigator", "javascript"]) and also the string "engineering". Explicitly using "engineering" in the constructor indicates that allEngineerobjects have the same value for the inheriteddeptproperty, and this value overrides the value inherited fromEmployee. - Because
baseis a method ofEngineer, within the call tobase, JavaScript binds thethiskeyword to the object created in Step 1. Thus, theWorkerBeefunction in turn passes the "Doe, Jane" and ["navigator", "javascript"] arguments to theEmployeeconstructor function. Upon return from theEmployeeconstructor function, theWorkerBeefunction uses the remaining argument to set theprojectsproperty. - Upon return from the
basemethod, theEngineerconstructor initializes the object'smachineproperty to "belau". - Upon return from the constructor, JavaScript assigns the new object to the
janevariable.
You might think that, having called the WorkerBee constructor from inside the Engineer constructor, you have set up inheritance appropriately for Engineer objects. This is not the case. Calling the WorkerBee constructor ensures that an Engineer object starts out with the properties specified in all constructor functions that are called. However, if you later add properties to the Employee or WorkerBee prototypes, those properties are not inherited by the Engineer object. For example, assume you have the following statements:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";
The jane object does not inherit the specialty property. You still need to explicitly set up the prototype to ensure dynamic inheritance. Assume instead you have these statements:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";
Now the value of the jane object's specialty property is "none".
Another way of inheriting is by using the .call/.apply methods. Below are equivalent:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
|
function Engineer (name, projs, mach) {
WorkerBee.call(this, name, "engineering", projs);
this.machine = mach || "";
}
|
Using the javascript .call method makes a cleaner implementation because the ".base" is not needed anymore.