Just JavaScript - Life Without Type - Duck Testing And Prototype Construction |
Written by Ian Elliot | ||||||||
Thursday, 07 May 2015 | ||||||||
Page 4 of 4
The Prototype As InterfaceSuppose 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:
and in the function you could check that the passed parameter had proto in its prototype chain something like:
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:
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:
Now you can add Proto to an existing object using:
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
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 TypeSo 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:
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
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:
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 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
then the Navigator pane shows the constructor object and its properties as if it was a class. When you start to use an object constructed by A NetBeans will offer you a list of properties that you can select from.
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
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
Related ArticlesLate Binding - Myths and Reality Just JavaScript - The Object Expression Covariance And Contravariance - A Simple Guide The Working Programmer's Guide To Language Paradigms 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, Facebook, Google+ or Linkedin, or sign up for our weekly newsletter.
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 ) |