More Flexible Constructors

  • Revision slug: JavaScript/Guide/Obsolete_Pages/The_Employee_Example/More_Flexible_Constructors
  • Revision title: More Flexible Constructors
  • Revision id: 117595
  • Created:
  • Creator: Johndr
  • Is current revision? No
  • Comment /* More Flexible Constructors */

Revision Content

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.

Image:hier05.gif
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 <tt>0</tt> (zero) and <tt>false</tt> 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.

Image:hier06.gif
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:

  1. The new operator creates a generic object and sets its __proto__ property to Engineer.prototype.
  2. The new operator passes the new object to the Engineer constructor as the value of the this keyword.
  3. The constructor creates a new property called base for that object and assigns the value of the WorkerBee constructor to the base property. This makes the WorkerBee constructor a method of the Engineer object.

    The name of the base property is not special. You can use any legal property name; base is simply evocative of its purpose.

  4. The constructor calls the base method, passing as its arguments two of the arguments passed to the constructor ("Doe, Jane" and {{mediawiki.external('\"navigator\", \"javascript\"')}}) and also the string "engineering". Explicitly using "engineering" in the constructor indicates that all Engineer objects have the same value for the inherited dept property, and this value overrides the value inherited from Employee.
  5. Because base is a method of Engineer, within the call to base, JavaScript binds the this keyword to the object created in Step 1. Thus, the WorkerBee function in turn passes the "Doe, Jane" and {{mediawiki.external('\"navigator\", \"javascript\"')}} arguments to the Employee constructor function. Upon return from the Employee constructor function, the WorkerBee function uses the remaining argument to set the projects property.
  6. Upon return from the base method, the Engineer constructor initializes the object's machine property to "belau".
  7. Upon return from the constructor, JavaScript assigns the new object to the jane variable.

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.

{{template.PreviousNext("Core_JavaScript_1.5_Guide:The_Employee_Example:Object_Properties:Adding_Properties", "Core_JavaScript_1.5_Guide:Property_Inheritance_Revisited")}}

{{ wiki.languages( { "ja": "ja/Core_JavaScript_1.5_Guide/The_Employee_Example/More_Flexible_Constructors", "pl": "pl/Przewodnik_po_j\u0119zyku_JavaScript_1.5/Praca_z_przyk\u0142adem/Wi\u0119cej_elastycznych_konstruktor\u00f3w" } ) }}

Revision Source

<div class="noinclude"></div>
<h3 name="More_Flexible_Constructors"> More Flexible Constructors </h3>
<p>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.
</p><p><img alt="Image:hier05.gif" src="File:en/Media_Gallery/Hier05.gif"><br>
<small><b>Figure 8.5: Specifying properties in a constructor, take 1</b></small>
</p><p>The following table shows the Java and JavaScript definitions for these objects.
</p>
<table class="fullwidth-table">
<tbody><tr>
<th>JavaScript</th>
<th>Java</th>
</tr>
<tr>
<td><pre>
function Employee (name, dept) {
  this.name = name || "";
  this.dept = dept || "general";
}
</pre></td>
<td><pre>
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;
   }
}
</pre></td>
</tr>
<tr>
<td><pre>
function WorkerBee (projs) {
this.projects = projs || [];
}
WorkerBee.prototype = new Employee;
</pre></td>
<td><pre>
public class WorkerBee extends Employee {
   public String[] projects;
   public WorkerBee () {
      this(new String[0]);
   }
   public WorkerBee (String[] projs) {
      this.projects = projs;
   }
}
</pre></td>
</tr>
<tr>
<td><pre> 
function Engineer (mach) {
   this.dept = "engineering";
   this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
</pre></td>
<td><pre>
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;
   }
}
</pre></td>
</tr>
</tbody></table>
<p><br>
These JavaScript definitions use a special idiom for setting default values:
</p>
<pre>this.name = name || "";
</pre>
<p>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. <i>Please note</i>: This may not work as expected with numeric or boolean arguments, as both <tt>0</tt> (zero) and <tt>false</tt> 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:
</p>
<pre>this.authorized = typeof(authorized) !== 'undefined' ? authorized : true;
</pre>
<p>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:
</p>
<pre>jane = new Engineer("belau");
</pre>
<p>Jane's properties are now:
</p>
<pre>jane.name == "";
jane.dept == "engineering";
jane.projects == [];
jane.machine == "belau"
</pre>
<p>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.
</p><p>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.
</p><p><img alt="Image:hier06.gif" src="File:en/Media_Gallery/Hier06.gif"><br>
<small><b>Figure 8.6    Specifying properties in a constructor, take 2</b></small>
</p><p>Let's look at one of these definitions in detail. Here's the new definition for the Engineer constructor:
</p>
<pre>function Engineer (name, projs, mach) {
  this.base = WorkerBee;
  this.base(name, "engineering", projs);
  this.machine = mach || "";
}
</pre>
<p>Suppose you create a new Engineer object as follows:
</p>
<pre>jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
</pre>
<p>JavaScript follows these steps:
</p>
<ol><li> The new operator creates a generic object and sets its __proto__ property to Engineer.prototype.
</li><li> The new operator passes the new object to the Engineer constructor as the value of the this keyword.
</li><li> <p>The constructor creates a new property called base for that object and assigns the value of the WorkerBee constructor to the base property. This makes the WorkerBee constructor a method of the Engineer object.</p><p>The name of the base property is not special. You can use any legal property name; base is simply evocative of its purpose.</p>
</li><li> The constructor calls the base method, passing as its arguments two of the arguments passed to the constructor ("Doe, Jane" and {{mediawiki.external('\"navigator\", \"javascript\"')}}) and also the string "engineering". Explicitly using "engineering" in the constructor indicates that all Engineer objects have the same value for the inherited dept property, and this value overrides the value inherited from Employee.
</li><li> Because base is a method of Engineer, within the call to base, JavaScript binds the this keyword to the object created in Step 1. Thus, the WorkerBee function in turn passes the "Doe, Jane" and {{mediawiki.external('\"navigator\", \"javascript\"')}} arguments to the Employee constructor function. Upon return from the Employee constructor function, the WorkerBee function uses the remaining argument to set the projects property.
</li><li> Upon return from the base method, the Engineer constructor initializes the object's machine property to "belau".
</li><li> Upon return from the constructor, JavaScript assigns the new object to the jane variable.
</li></ol>
<p>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:
</p>
<pre>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";
</pre>
<p>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:
</p>
<pre>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";
</pre>
<p>Now the value of the jane object's specialty property is "none".
</p>
<hr>
<p>Another way of inheriting is by using the .call/.apply methods. Below are equivalent:
</p>
<table><tbody><tr><td>
<pre class="eval">function Engineer (name, projs, mach) {
  this.base = WorkerBee;
  this.base(name, "engineering", projs);
  this.machine = mach || "";
}
</pre>
</td><td>
<pre class="eval">function Engineer (name, projs, mach) {
  WorkerBee.call(this, name, "engineering", projs);
  this.machine = mach || "";
}
</pre>
</td></tr></tbody></table>
<p>Using the javascript .call method makes a cleaner implementation because the ".base" is not needed anymore.
</p>
<div class="noinclude">
<p>{{template.PreviousNext("Core_JavaScript_1.5_Guide:The_Employee_Example:Object_Properties:Adding_Properties", "Core_JavaScript_1.5_Guide:Property_Inheritance_Revisited")}}
</p>
</div>
{{ wiki.languages( { "ja": "ja/Core_JavaScript_1.5_Guide/The_Employee_Example/More_Flexible_Constructors", "pl": "pl/Przewodnik_po_j\u0119zyku_JavaScript_1.5/Praca_z_przyk\u0142adem/Wi\u0119cej_elastycznych_konstruktor\u00f3w" } ) }}
Revert to this revision