Just JavaScript - Object Factories |
Written by Ian Elliot | |||
Monday, 25 March 2019 | |||
Page 2 of 2
At their very simplest an object factory creates an object literal and returns it as its result. For example an object factory for the point object is: var Point=function(){ return {x:0,y:0}; }; and to use it to create an instance of the Point object you would use: var myPoint=Point(); It seems trivial yet in no time at all it can become complicated and subtle. Notice that we already have recreated one of the minor problems of a class-based language - finding a name for the class and for an instance. In this case the naming problem is finding something to call the object factory and then names for the instances - i.e. Point and myPoint. Perhaps it would be better to call the factory PointMaker or something to indicate that it wasn't actually an instance of a Point object. As the object being created gets bigger and more complicated, building it in smaller steps becomes a better method. Starting from an empty object you can add properties one by one. For example: var Point=function(){ var obj={}; obj.x=0; obj.y=0; return obj; }; One of the big advantages of using an object factory is that you can use parameters to customize the object produced. For example: var Point=function(x,y){ var obj={}; obj.x=x; obj.y=y; return obj; }; and you create the object using: var myPoint=Point(1,2); The ability to customize object instances is one of the big plus points of using an object factory. It is so useful that an object factory is often used to create a singleton – even though the factory will only ever create a single object. Constructing MethodsOf course the object that the factory produces can have any type of object as its properties including Function objects. As already explained in Chapter 8 function properties aren't really the same thing as methods. To make a Function property act like a method you have to make use of the this call context, i.e. this, to specify the instance that is being referenced. If you are creating an object as an object literal there is no other choice but to use this to create a late bound method for an object. However, if you are using a factory function there is an alternative and it is arguably better as it creates an early bound method. For example, suppose we want to write a method that adds x and y together: var Point=function(x,y){ var obj={}; obj.x=x; obj.y=y; obj.add=function(){ return obj.x+obj.y; }; return obj; }; Notice that the function uses obj.x and obj.y not just x and y. If you try out: var myPoint=Point(1,2); myPoint.x=4; alert(myPoint.add()); you will find it works and displays 6 – but how can it? Private VariablesWhen myPoint.add is called, the variable obj has long gone as the factory object's code has terminated and all its variables have been destroyed. How can a Function object make use of the factory's obj variable long after it has been destroyed? The answer, of course, is closure. If you recall, closure in JavaScript is necessary to resolve the problem of having Function objects which live for longer than their code. When you define the add Function object it captures the variables that are in scope at the time of its creation, i.e. obj. Thus the add function has access to obj even though, without closure, it should have been destroyed. Also notice that code in other Function objects can't access the obj variable because it no longer exists and it is local to the factory function. That is, obj is a private variable for the instance of the point object we have created. The general rule is that any local variables declared within the object factory are turned into private variables for the object created by closure. More exactly all of the methods created are assigned the same execution context as a closure. This means that all of the methods share the same private variables which is usually what you would expect and require. This is another advantage of using an object factory even for a singleton. Object literals don't have private variables unless they are created within a function. Private PropertiesIt should be obvious now that closure can be used to create private properties for objects, but it is worth exploring a little more. For example, you can create a private property for the point object using something like: var Point=function(x,y){ var obj={}; var myPrivateData=10; obj.getPriv= function(){ return myPrivateData; }; return obj; }; var myPoint= Point(1,2); alert(myPoint.getPriv()); Where the code has been stripped down to just what is needed to demo a private variable. In this case the getPriv function can access myPrivateData because of closure and so everything works. However, any attempt to directly access the variable fails because there is no closure in operation and it is not a property of the constructed object. That is both: alert(myPoint.myPrivateData); and: alert(myPrivateData); fail. You can of course use a getter and setter with a private variable but it is slightly more complicated to add them using an object factory. For example: var Point=function(x,y){ var obj={ get Priv(){return myPrivateData;} }; var myPrivateData=10; return obj; }; Now you can use Priv as if it was a variable but there is no access to myPrivate data except through get or set: var myPoint= Point(1,2); alert(myPoint.Priv); The private variable pattern is well known, but it works for methods as well although not as commonly. For example, if you want to create a private function that only the object can use, all you have to do is declare it as a local variable within the object factory: Point=function(x,y){ var myPrivateData=10; var obj={}; var myPrivateAdd=function(a,b){ return a+b; }; In this case myPrivateAdd is accessible from the object constructed because of closure, which means it can be called from within any method of the object. Again the myPrivateAdd function cannot be called outside the object because it is not a property and is only available as a closure inside the object. Notice that a private function can make use of private variables as these are made available to it by closures within the object itself. The private variables and functions are instance entities in the sense that each object that the factory function produces has its own copy of all of the private entities because, as already explained, the execution context is created each time the factory function is used. That is, all of the private variables are not shared. If you want to make a private method, then you can use the obj variable within the private function to access properties of the object. For example: var Point = function (x, y) { var obj = {}; obj.x = x; obj.y = y You can see that myPrivateAdd is private and can be used by the add method to add two properties of the Point object. This is a private method. Remaining Sections In The Chapter
Summary
This is an extract from the book Just JavaScript by Ian Elliot. Buy Now: from your local AmazonJust JavaScript
|
|||
Last Updated ( Monday, 25 March 2019 ) |