Just JavaScript - Life Without Type - Duck Testing And Prototype Construction
Written by Ian Elliot   
Thursday, 07 May 2015
Article Index
Just JavaScript - Life Without Type - Duck Testing And Prototype Construction
Prototype Testing
Prototype Constructor
Prototype as Interface

The Prototype As Interface

Suppose you have a function that accepts an object and does something specific with it that depends on the object having particular properties. Then you could ensure that the object passed to the function had those properties by insisting that it had a particular object with those properties in its prototype chain. 

For example, if you need properties x and y then first create an object with x and y:

var proto={x:0,y:0};

and in the function you could check that the passed parameter had proto in its prototype chain something like:

var myfunction= function(obj){
 if(!proto.isPrototypeOf(obj))return:
..

 

This looks a bit like using an interface in other languages but it doesn't work in JavaScript.

You can't simply write a function that demands that an object passed to it has a specified object in its prototype chain.

The reason is that the only way to get an object into the chain is to set it as the immediate prototype object which means you set an entire prototype chain not just add one object.

That is suppose you want your object to have a protype chain that has objects a and b in it. Then you can't use proto as your object's prototype because it doesn't have a and b in its prototype chain and there is no way to ensure that it does. 

You also can't add proto to an existing object's prototype chain. Once the prototype is set there is no standard way to modify it.

To create an interface like mechanism you need a way to add a single object to an existing prototype chain and there seems to be no way to do this - but there is.

That is you can ask that a specific object is in the prototype chain irrespective of what is already in the chain. 

 

All you have to do is add the required object at the start of the existing chain rather than at the end. 

For example, to add proto, with constructor Proto, to the prototype chain of the already created object referenced by obj you could use:

Proto.prototype=obj;
obj=new Proto();

As first this looks slightly crazy but if you think about it then you should be able to see that it doesn't matter where an object is in the prototype chain. The only thing this determines is which property of multiply defined properties takes precedence - the first in the chain is always used.

In this case we create a new Proto object and use obj as its prototype. Now we have an object referenced by obj that has all of the properties of Proto and all of the properties that obj had previously.

It is effectively adding the properties of Proto to the existing object. 

You can bundle this up into a function if you want to:

var Proto=function(o){
                   var temp=function(){
                             this.x=1;
                             this.y=2;
                            };
                    temp.prototype=o;
                    return new temp();
                   };

Now you can add Proto to an existing object using:

obj=Proto(obj);

Having to repeat the obj variable is a bit ugly but it works.

Notice that if you add Proto to a number of objects each gets its own object - they do not share a single object as they would in normal prototype inheritance. You also can't test for Proto being in the prototype chain - because of course it isn't. You could test for the constructor being Proto but you would have to remember to set the prototype constructor property. 

Using this approach you can add any number of objects into the prototype chain of existing object and you can even keep references to the un-modified object. For example

var obj2=Proto(obj);

gives you obj2 as a reference to the new object including Proto and obj references the original object without Proto. 

You can use this technique to create analogs of multiple inheritance, mixins, or interfaces depending on how you think about it and the fine details of implementation.  

Edit Time Type

So far what we have been concentrating on is the way JavaScript handles runtime inheritance via the prototype mechanism.

Many of the techniques may seem overly complicated but this is because we are trying to recreate facilities that are available in class based languages. Most of the time you can ignore issues of "type" because JavaScript objects generally don't use complex prototype chains and hardly use concepts such as inheritance. Most of the time you don't have to do runtime duck testing because the code sets the object in such away that it cannot fail.

Put simply if the code works just once it will always work. 

You can think of this sort of runtime error as a deterministic runtime error because it you don't correct it then it will always happen. 

For example if you write:

var a=new A();

then it is clear that a references an object constructed by A. If you then go on and use just the properties of A then the code will run. If it doesn't you need to correct what would be a compile type type error in a strongly typed language. If you don't correct the error then it will happen every time you run the program.

Even when you write a function with a untyped parameter in JavaScript 

var myFunc=function(x){...};

then there are many situations where if the program works once it will always work.

For example, if the function uses x as if it was an a and the code is:

var a=new A();
function(a){...}

There just isn't enough variability in the code for it to subsequently fail if it works even once. Looking at it another way a static type error is easy to find and easy to correct.

To find a "type" error that doesn't correspond to a deterministic runtime error you need an extra source of entropy.

Either some external source is setting the object that a variable references or the object really is being generated randomly in some sense. The external source could be something like when you accept a JSON coded data structure from a server, reading data from a file or any real world inputs. It is a long understood idea that you need to protect a program from input data - and yes here you need duck testing. 

Random sources of objects are far less commonly encountered but they can be created both in strongly typed languages and JavaScript.

For example:

if(math.random()>0.5){
  x=new A();
}else{
  x=new B();
};
myFunc(x);

If myFunc expects x to be an A then the program will crash 50% of the time. Notice that this code can be written in a strongly typed language using Object as a type that can be used to reference any type and by making myFunc cast x to an A. 

In most cases errors relating to "type" generate deterministic runtime errors and this sort of random behaviour is rare. 

What this means is that to catch such errors in an untyped language like JavaScript you have to perform testing that covers all of the code. 

To make it easier to write code that doesn't fail at run time the IDE usually can provide some help.Class based languages often have editors that give you hints about what properties an object has. They can do this because the class that defines the object is essentially an inventory of its properties.

JavaScript IDEs can do the same thing by deducing the properties that an object has. For example in NetBeans if you enter a constructor

var A=function(){
 this.x=10;
 this.y=20;
};

then the Navigator pane shows the constructor object and its properties as if it was a class. 

nav

When you start to use an object constructed by A NetBeans will offer you a list of properties that you can select from. 

 

prompt

 

This is usually referred to as type inference but what is going on is the inference of the object and its properties that a variable references. 

Of course, there are lots of places where this goes wrong. For example, and this is the most common and worrying, when you write a function there is no way for the IDE to work out the properties of any of the parameters. That is in 

function(x){...}

x really is unknown. 

At this point people often suggest that annotation is the solution, but this is simply reinventing type. A better solution might be to have a facility to simply tell the IDE the expected type of the variable using an interactive UI. 

Perhaps this is the moment when, if you really want the simplicity of a typeless dynamic programming environment, it is time to give up demanding the conveniences of type.   

 

Summary 

  • JavaScript doesn't have a compile time and doesn't have type so all of the checks that would often be done by compile time type checking have to be performed at runtime. 

  • Duck testing is the simplest approach to ensuring that an object has the properties you want to use.

  • Use typeof to determine primitive type when necessary.

  • Use try-catch to ensure that a method does what you expect.

  • You can use getPrototypeOf and isPrototypeOf to test a prototype chain for the presence of an object that acts in place of type. However, this doesn't work well because an object is not part of its own prototype chain.

  • Using prototype constructors, which always return an empty object with an extended prototype chain, solves all of the problems in using getPrototypeOf and isPrototypeOf.

  • You can add a single object to the prototype chain of an existing object to create an "interface" like mechanism by adding to the front of the chain. 

  • Tools can help with writing correct JavaScript without the need to modify the language and without the need to give up the advantages of an untyped dynamic language. 

 

 

 

Related Articles

Late Binding - Myths and Reality

Just JavaScript - The Object Expression       

Covariance And Contravariance - A Simple Guide       

The Working Programmer's Guide To Language Paradigms       

Data Typing Is A Relic       

Strong Typing       

Weakly Typed Languages    

Type Systems Demystified         

Casting – the escape from strong typing       

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, FacebookGoogle+ or Linkedin,  or sign up for our weekly newsletter.

 

Banner


JavaScript Canvas - Fetch API

Working with lower-level data is very much part of graphics. This extract from Ian Elliot's book on JavaScript Graphics looks at how to use typed arrays to access graphic data.



JavaScript Jems - The Inheritance Tax

JavaScript should not be judged as if it was a poor version of the other popular languages - it isn't a Java or a C++ clone. It does things its own way.  In particular, it doesn't do inheritance  [ ... ]


Other Articles

 

espbook

 

Comments




or email your comment to: comments@i-programmer.info

<ASIN:0596805527>

<ASIN:193398869X>

<ASIN:0137054890>

<ASIN:1449381871>

<ASIN:1430230541>

 



Last Updated ( Tuesday, 25 August 2015 )