understanding simple class emulator in JavaScript - javascript

Recently I started to learn a bit more advanced JavaScript (as far I only used jQuery for some simple tasks) and bought a book of Alex MaxCaw "JavaScript Web Applications". The first chapter treats about creating simple class emulator. I understand almost everything except for two lines of code marked with comments down below:
var Class = function(parent) {
var _class = function() {
this.init.apply(this, arguments);
};
if(parent) {
var subclass = function() {};
subclass.prototype = parent.prototype;
_class.prototype = new subclass();
};
_class.prototype.init = function() {};
_class.fn = _class.prototype;
//????
_class.fn.parent = _class;
//????
_class._super = _class.__proto__;
return _class;
};
Can anyone tell me what is purpose of these two lines? I'll be very thankfull.

Walking through the code:
Class is defined as a function that calls init with the arguments provided it. This means you can call it with standard constructor syntax using new eg. var instance = new Thingy() and get the init function called with the proper this value.
If you pass a parent class in, your class gets that class's prototype property added to the prototype chain of a new empty object which it uses as its prototype property. A more succinct way of doing this in modern browsers is _class.prototype = Object.create(parent.prototype);
The init function is defined. This should likely be overridden with more useful initialization code after a _class instance is created (or the code should be changed to allow the passing in of an init function when you create a Class... or allow the instance to walk the prototype chain to look for other init functions.
_class.fn is created to provide a reference to the _class constructor's prototype function.
_class.fn.parent is created to provide a reference back to the constructor. This may be useful if you are applying the prototype in some other context and want a reference back to the prototype's constructor.
_class._super is assigned the internal, non-standard __proto__ property of the constructor. Remember that constructors are functions and, in Javascript, functions are objects. This means they have their own internal prototypes. The earlier references to prototype are the prototype assigned to objects created with this constructor NOT the constructor's prototype itself. All functions inherit from Function.prototype, which is where they get bind, apply, etc. _super in this case is just a reference to Function.prototype.
As to when this type of _super is used, one could imagine doing the following:
function Maker(){ //this will be called as a constructor, ie. with new
var fun = function(){}; //Make a function
fun.__proto__ = this.__proto__; //yuck. Set the function's this value to the instance
return fun; //return the function
}
Maker.prototype={say:function(){console.log("Javascript is fun!.. And weird.")}};
var fun = new Maker();
fun.say() //"Javascript is fun!.. And weird."
console.log(fun.__proto__) // Object{say:function}
console.log(fun.bind) // undefined!!
Woah! What just happened?
In fact, you replaced your functions internal prototype with an Object. This allows you to build up interesting prototype chains and interact with both functions and objects in a similar way. Note, however, that the link with Function.prototype has been severed, which is why we don't have access to bind. However let's fix it with more prototype magic!
function FunctionConnector(obj){
for (var prop in obj){
if(obj.hasOwnProperty(prop){
this.prop=obj.prop
}
}
}
FunctionConnector.prototype=Function.prototype;
Maker.prototype=new FunctionConnector({say:function(){
console.log("Javascript is fun!.. And weird.")
}});
var fun = new Maker();
fun.say() //"Javascript is fun!.. And weird."
console.log(fun.__proto__) // Object{say:function}
console.log(fun.bind) // function bind(){ [native code] }
Now what is that FunctionConnector? It takes an object and, when called as a constructor, returns an object that both has all the properties of the passed object AND inherits from Function.prototype. As you can see, our access to bind has returned (Of course we also could have made do with our original implementation and just Function.prototype.bind.called our way to victory).
With this new pattern in hand it may be clearer what _super in your code does, namely it references the built in prototype of the _class constructor you are making (In our example the instance of FunctionConnector would be _super). This reference could be used to patch the prototype at runtime, call methods with apply or anything else you can due with a reference to an object.
This is, as you may have noticed a little hackish, especially since __proto__ is nonstandard. But it's also somewhat neat if you enjoy the patterns it allows. I'd recommend only doing something like this if you are very confident in your knowledge of Javascript inheritance, and perhaps not even then unless you are in charge of your entire code base.

From what i know, fn is just an alias to the prototype property
And about the _super, that one is for referencing to the "class" from which you are inheriting
Here's more about the use of _super and js inheritance: article

Related

Setting the prototype has to be done before the first call to constructor function. But why...? [duplicate]

TL;DR? Why can't I overwrite a constructor's prototype from within the constructor?
I'm figuring out my pattern for prototypical inheritance. I don't like how prototypes are usually defined externally from a constructor, and want to logically encapsulate things better.
I found that the one magical line that I expected to work, doesn't.
function Orifice(){
this.exhaust=function(){};
this.ingest=function(){};
}
var standardOrifice = new Orifice();
function Sphincter(){
this.constructor.prototype = standardOrifice; // <-- does not work
this.relax=function(){};
this.tighten=function(){};
}
Interestingly, I can write individual properties to this.constructor.prototype, but I cannot overwrite the whole prototype object the same way one can outside of a constructor's definition.
So stuff like this works:
this.constructor.prototype.exhaust = standardOrifice.exhaust;
this.constructor.prototype.ingest = standardOrifice.ingest;
For which I can create a simple clone function to handle this:
function extend(target){
return {
from: function(obj){
target.__proto__ = obj.constructor.prototype;
for (key in obj) if (obj.hasOwnProperty(key)) target[key]=obj[key];
return target;
}
};
}
Thankfully in my tests so far, this technique appears to work well, though I'm not sure if there are details or performance cases I could be missing.
function Sphincter(){
extend(this.constructor.prototype).from(standardOrifice);
//...
}
Why can't I overwrite a constructor's prototype from within the constructor? Yet I can outside the constructor? And writing properties individually works from within a constructor?
Why can't I overwrite a constructor's prototype from within the constructor?
You can, but it's too late. The new instance has already been generated, inheriting from the old prototype. Maybe read how new works.
I don't like how prototypes are usually defined externally from a constructor.
That's just the way it is. You really should not setup the prototype from within the constructor - it would be executed everytime a new instance is created. That's specifically what prototypes are not supposed to be. See also Assigning prototype methods *inside* the constructor function - why not?
and want to logically encapsulate things better.
You might want to have a look at the various (revealing) module patterns. Or maybe even at some Class framework.
I'm currently looking for more concrete reasons that I should not go forth with the pattern I've been presenting.
It does not work in Internet Explorer. It would not work in any ES5-compliant environment that does not support the __proto__ property. You should never use it set a prototype on an existing object. Instead, use Object.create (or its shim) for Correct javascript inheritance - which requires that you overwrite the prototype outside of the constructor.
My suggestion is to call your extend helper outside the constructor on it, which still has a nice syntax.
Answer to the Specific Question
Why can't I overwrite a constructor's prototype from within the constructor?
It's because constructors are actually called after your object has already been instantiated. And since your object has managed to instantiate before your constructor has touched anything, your constructor has also already been assigned a "default" prototype.
Adding properties to this.constructor.prototype seems to work -- because you're actually manipulating the constructor's pre-assigned default prototype object, which all of your instances inherit from.
In my examples, this.constructor.prototype ended up referring to the default-assigned prototype of the constructor: so wholly overwriting it meant all new instances from that moment onward would have that new prototype -- as Bergi said, "too late" -- your current instance would not have that new prototype, as it still has the old default-assigned prototype because it's already been instantiated.
A Better Pattern for Avoiding Nonsense
I've come to understand, that the techniques presented in my question simply won't do. The question itself is generally misguided. By combining Bergi's wisdom with my own personal biases, I've come up with this pattern as a means to avoid having to find an answer to the original question altogether:
function extend(p){
return { to: function(C){ for (k in p) if (p.hasOwnProperty(k))
C.prototype[k]=p[k]; return C; } };
};
var orifice = new function Orifice(){
this.exhaust=function(){};
this.ingest=function(){};
};
var Sphincter = extend(orifice).to(function Sphincter(){
this.relax=function(){};
this.tighten=function(){};
});
Here's the extend function, expanded:
function extend(parentObject){
return {
to: function(ChildConstructor){
for (key in parentObject)
if (parentObject.hasOwnProperty(key))
ChildConstructor.prototype[key] = parentObject[key];
return ChildConstructor;
}
};
};
I used this to test that it works:
// TESTING
var s=new Sphincter();
var tests=['relax','tighten','exhaust','ingest'];
for (var i in tests) console.log("s."+tests[i]+"() is "+(tests[i]in s?"present :)":"MISSING!"));

Assign inheritance chain inside constructor through prototype [duplicate]

TL;DR? Why can't I overwrite a constructor's prototype from within the constructor?
I'm figuring out my pattern for prototypical inheritance. I don't like how prototypes are usually defined externally from a constructor, and want to logically encapsulate things better.
I found that the one magical line that I expected to work, doesn't.
function Orifice(){
this.exhaust=function(){};
this.ingest=function(){};
}
var standardOrifice = new Orifice();
function Sphincter(){
this.constructor.prototype = standardOrifice; // <-- does not work
this.relax=function(){};
this.tighten=function(){};
}
Interestingly, I can write individual properties to this.constructor.prototype, but I cannot overwrite the whole prototype object the same way one can outside of a constructor's definition.
So stuff like this works:
this.constructor.prototype.exhaust = standardOrifice.exhaust;
this.constructor.prototype.ingest = standardOrifice.ingest;
For which I can create a simple clone function to handle this:
function extend(target){
return {
from: function(obj){
target.__proto__ = obj.constructor.prototype;
for (key in obj) if (obj.hasOwnProperty(key)) target[key]=obj[key];
return target;
}
};
}
Thankfully in my tests so far, this technique appears to work well, though I'm not sure if there are details or performance cases I could be missing.
function Sphincter(){
extend(this.constructor.prototype).from(standardOrifice);
//...
}
Why can't I overwrite a constructor's prototype from within the constructor? Yet I can outside the constructor? And writing properties individually works from within a constructor?
Why can't I overwrite a constructor's prototype from within the constructor?
You can, but it's too late. The new instance has already been generated, inheriting from the old prototype. Maybe read how new works.
I don't like how prototypes are usually defined externally from a constructor.
That's just the way it is. You really should not setup the prototype from within the constructor - it would be executed everytime a new instance is created. That's specifically what prototypes are not supposed to be. See also Assigning prototype methods *inside* the constructor function - why not?
and want to logically encapsulate things better.
You might want to have a look at the various (revealing) module patterns. Or maybe even at some Class framework.
I'm currently looking for more concrete reasons that I should not go forth with the pattern I've been presenting.
It does not work in Internet Explorer. It would not work in any ES5-compliant environment that does not support the __proto__ property. You should never use it set a prototype on an existing object. Instead, use Object.create (or its shim) for Correct javascript inheritance - which requires that you overwrite the prototype outside of the constructor.
My suggestion is to call your extend helper outside the constructor on it, which still has a nice syntax.
Answer to the Specific Question
Why can't I overwrite a constructor's prototype from within the constructor?
It's because constructors are actually called after your object has already been instantiated. And since your object has managed to instantiate before your constructor has touched anything, your constructor has also already been assigned a "default" prototype.
Adding properties to this.constructor.prototype seems to work -- because you're actually manipulating the constructor's pre-assigned default prototype object, which all of your instances inherit from.
In my examples, this.constructor.prototype ended up referring to the default-assigned prototype of the constructor: so wholly overwriting it meant all new instances from that moment onward would have that new prototype -- as Bergi said, "too late" -- your current instance would not have that new prototype, as it still has the old default-assigned prototype because it's already been instantiated.
A Better Pattern for Avoiding Nonsense
I've come to understand, that the techniques presented in my question simply won't do. The question itself is generally misguided. By combining Bergi's wisdom with my own personal biases, I've come up with this pattern as a means to avoid having to find an answer to the original question altogether:
function extend(p){
return { to: function(C){ for (k in p) if (p.hasOwnProperty(k))
C.prototype[k]=p[k]; return C; } };
};
var orifice = new function Orifice(){
this.exhaust=function(){};
this.ingest=function(){};
};
var Sphincter = extend(orifice).to(function Sphincter(){
this.relax=function(){};
this.tighten=function(){};
});
Here's the extend function, expanded:
function extend(parentObject){
return {
to: function(ChildConstructor){
for (key in parentObject)
if (parentObject.hasOwnProperty(key))
ChildConstructor.prototype[key] = parentObject[key];
return ChildConstructor;
}
};
};
I used this to test that it works:
// TESTING
var s=new Sphincter();
var tests=['relax','tighten','exhaust','ingest'];
for (var i in tests) console.log("s."+tests[i]+"() is "+(tests[i]in s?"present :)":"MISSING!"));

this.constructor.prototype -- can't wholly overwrite, but can write individual props?

TL;DR? Why can't I overwrite a constructor's prototype from within the constructor?
I'm figuring out my pattern for prototypical inheritance. I don't like how prototypes are usually defined externally from a constructor, and want to logically encapsulate things better.
I found that the one magical line that I expected to work, doesn't.
function Orifice(){
this.exhaust=function(){};
this.ingest=function(){};
}
var standardOrifice = new Orifice();
function Sphincter(){
this.constructor.prototype = standardOrifice; // <-- does not work
this.relax=function(){};
this.tighten=function(){};
}
Interestingly, I can write individual properties to this.constructor.prototype, but I cannot overwrite the whole prototype object the same way one can outside of a constructor's definition.
So stuff like this works:
this.constructor.prototype.exhaust = standardOrifice.exhaust;
this.constructor.prototype.ingest = standardOrifice.ingest;
For which I can create a simple clone function to handle this:
function extend(target){
return {
from: function(obj){
target.__proto__ = obj.constructor.prototype;
for (key in obj) if (obj.hasOwnProperty(key)) target[key]=obj[key];
return target;
}
};
}
Thankfully in my tests so far, this technique appears to work well, though I'm not sure if there are details or performance cases I could be missing.
function Sphincter(){
extend(this.constructor.prototype).from(standardOrifice);
//...
}
Why can't I overwrite a constructor's prototype from within the constructor? Yet I can outside the constructor? And writing properties individually works from within a constructor?
Why can't I overwrite a constructor's prototype from within the constructor?
You can, but it's too late. The new instance has already been generated, inheriting from the old prototype. Maybe read how new works.
I don't like how prototypes are usually defined externally from a constructor.
That's just the way it is. You really should not setup the prototype from within the constructor - it would be executed everytime a new instance is created. That's specifically what prototypes are not supposed to be. See also Assigning prototype methods *inside* the constructor function - why not?
and want to logically encapsulate things better.
You might want to have a look at the various (revealing) module patterns. Or maybe even at some Class framework.
I'm currently looking for more concrete reasons that I should not go forth with the pattern I've been presenting.
It does not work in Internet Explorer. It would not work in any ES5-compliant environment that does not support the __proto__ property. You should never use it set a prototype on an existing object. Instead, use Object.create (or its shim) for Correct javascript inheritance - which requires that you overwrite the prototype outside of the constructor.
My suggestion is to call your extend helper outside the constructor on it, which still has a nice syntax.
Answer to the Specific Question
Why can't I overwrite a constructor's prototype from within the constructor?
It's because constructors are actually called after your object has already been instantiated. And since your object has managed to instantiate before your constructor has touched anything, your constructor has also already been assigned a "default" prototype.
Adding properties to this.constructor.prototype seems to work -- because you're actually manipulating the constructor's pre-assigned default prototype object, which all of your instances inherit from.
In my examples, this.constructor.prototype ended up referring to the default-assigned prototype of the constructor: so wholly overwriting it meant all new instances from that moment onward would have that new prototype -- as Bergi said, "too late" -- your current instance would not have that new prototype, as it still has the old default-assigned prototype because it's already been instantiated.
A Better Pattern for Avoiding Nonsense
I've come to understand, that the techniques presented in my question simply won't do. The question itself is generally misguided. By combining Bergi's wisdom with my own personal biases, I've come up with this pattern as a means to avoid having to find an answer to the original question altogether:
function extend(p){
return { to: function(C){ for (k in p) if (p.hasOwnProperty(k))
C.prototype[k]=p[k]; return C; } };
};
var orifice = new function Orifice(){
this.exhaust=function(){};
this.ingest=function(){};
};
var Sphincter = extend(orifice).to(function Sphincter(){
this.relax=function(){};
this.tighten=function(){};
});
Here's the extend function, expanded:
function extend(parentObject){
return {
to: function(ChildConstructor){
for (key in parentObject)
if (parentObject.hasOwnProperty(key))
ChildConstructor.prototype[key] = parentObject[key];
return ChildConstructor;
}
};
};
I used this to test that it works:
// TESTING
var s=new Sphincter();
var tests=['relax','tighten','exhaust','ingest'];
for (var i in tests) console.log("s."+tests[i]+"() is "+(tests[i]in s?"present :)":"MISSING!"));

Purpose of this Javascript prototype snippet

Sorry I can't phrase this better. But I ran across some code like the following:
MyObject.prototype = Object.create(MyObject.prototype);
MyObject.prototype.constructor = MyObject;
And I just can't seem to figure out what it does. MyObject is defined above it something like this:
function MyObject(options) {
this.someProp = someDefault;
this.otherProp = process(options.something);
// etc...
}
and it's always called as a constructor. I'm just wondering what benefit those first two lines provide and if it's a known pattern in Javascript.
I just can't seem to figure out what it does
It creates a new object that inherits from [the old] MyObject.prototype via Object.create and then overwrites MyObject.prototype with that. It also explicitly adds a .constructor property which actually should be existing already.
I'm just wondering what benefit those first two lines provide
None, unless before that snippet someone has corrupted the prototype (like MyObject.prototype = Object.prototype) and this is an attempt to fix it.
…and if it's a known pattern in Javascript.
Not like this. Using Object.create to set up the prototype chain for inheritance between constructor-defined "classes" is a known pattern, but then the constructors would be different on each side of the assignment.
The two lines of code provided seem to be an incorrect attempt of the use of prototypal inheritance, but I see where you're going with this and what you're trying to accomplish.
As we know, there are two ways in JavaScript to define objects that have properties and methods as members - the object literal notation and function notation. Using object literal notation, we don't have immediate access to the new keyword (think of this like using abstract classes in Java or C#). With function notation, we have access to the new keyword because the initial declaration of an object as a function serves as our constructor.
In ECMAScript 5, The Object object was given a method called create that provided developers a simple way to create a new object from an existing object declared with the object literal notation. (See documentation here). However, objects created in function notation have problems with this method because they are Function objects. The Object.create method is a great way to use simple inheritance, allowing access to the base properties and methods.
With function notation, once the new keyword is used, the result is not a function, but rather an object. For example, I can test this:
var Obj = function(){};
console.log(typeof Obj) // "function"
console.log(typeof new Object()); // "object"
Because of this, you can only inherit once (meaning the child object cannot be derived from):
var MyObject = new Object();
var anotherObj = new MyObject() // throws exception
To alleviate this problem, you need to follow three steps:
Create your child object in function notation (so you can create new instances of it using the new keyword and inherit from it).
Set the child object's prototype (an object) to the result of a new instance of the base object (which will be an object as well).
Set the constructor of the child object (which happens to be on the object's prototype) back to reference the Function of itself (which is a function prior to instantiation). If you don't do this, the constructor will remain an object, which cannot spawn new instances.
From here, you can create new instances of both the child and parent objects and derive from both, using the pattern. Here's a practical example:
var Vehicle = function(){};
Vehicle.prototype.start = function() {
return this.make + " " + this.model + " " + "started";
}
var Car = function(color, make, model) {
this.color = color;
this.make = make;
this.model = model;
}
Car.prototype = new Vehicle();
Car.prototype.constructor = Car;
var myCar = new Car("red", "chevy", "aveo");
myCar.start(); //"chevy aveo started"
I really don't see any benefit in doing that.
What it's doing is providing the new object with the previous objects methods. But it's coming from the same object...
Here is a good example of JS inheritance:
http://jsfiddle.net/aDCmA/2/
var App = (function(){
var Being = function() {
this.living = true;
this.breathes = function () {
return true;
};
};
var Robert = function() {
this.blogs = true;
this.getsBored = function () {
return "You betcha";
}
};
Robert.prototype = new Being();
return {
Being: Being,
Robert: Robert,
being: function(){ return new Being(); },
robert: function(){ return new Robert(); }
}
}());
Here is another question that is similar: inherit prototype methods from other classes without overriding own prototype methods
Credit to Robert Nyman for originally blogging about it: http://robertnyman.com/2008/10/06/javascript-inheritance-how-and-why/
Let's see line by line:
MyObject.prototype = Object.create(MyObject.prototype);
This redefines MyObject.prototype to an object that inherits from MyObject.prototype. This is unusual, because it makes no sense to inherit from itself.
MyObject.prototype.constructor = MyObject;
Since the previous line overwrote MyObject.prototype, this is just fixing the constructor property that was lost in the process.
I can think of one scenario where tht might be useful: if some code before that messed up with MyObject.prototype, for example assigning the prototype of another constructor to it:
MyObject.prototype = SomethingElse.prototype; // wrong way to do inheritance.
Then the code you posted would be an attempt to fix it.
This is perfectly valid Javascript.
Any javascript function (say Func)can be used as a constructor and the constructor invocation also requires a prototype property (i.e. F.prototype or the prototype associated with the function) . Thus (almost) every function has a prototype property. The value of this property (i.e. Func.prototype).
Now the value of this prototype associated with the function is an object itself that has a single non enumerable property called constructor. And the value of this constructor property is the function object (i.e. F itself).
Lets take an example.
Say I construct a function Func
var Func = function() {
//your definition
};
Now since this can be invoked as a constructor it has to have a prototype property Func.prototype lets call this proto.
proto = Func.prototype;
Now the prototype has a single property (that is non enumerable) called constructor. This constructor has a value that is equal to the function object itself.
Dont believe me check it like this
Func.prototype.constructor === Func // =>true
Will always return true for any function.
Now from the code you explained :
So basically these two lines
MyObject.prototype = Object.create(MyObject.prototype);
MyObject.prototype.constructor = MyObject;
are modifying the value of the prototye to have a constructor property with the value of MyObject that is defined. But that would have happened anyways in the normal course of things. But the reason could be that maybe the prototype of the object has been changed earlier from the class it has been inherited from. In that case would those two lines make sense.
Hope that helps :)

JavaScript Code Architecture - Use Constructor Functions Or Not

Please help me decide whether I should use a function's prototype object and the "new" keyword or completely stay away from constructor functions.
Situation:
Function called widget() that will be called 10-15 times to initialize each widget on the page. widget() contains quite a few internal methods.
Each time widget() is called, the function needs to return an object that acts as an API to operate on the widget.
Question
1) Do I put all the internal methods inside Widget() under its prototype property? It does not make sense but the main reason for this is to not re-instantiate the internal functions every time widget() is called.
But if I do put the internal functions in prototype, each instantiated w object (w = new Widget();) has access to internal private methods.
2) If I stay away from constructor functions and new keyword and structure my code as down below, how do I fix the performance concern of the internal functions getting re-instantiated every time widget() is called.
function widget()
{
var returnObj = {};
/* Add internal functions but this will be re-instantiated every time */
return returnObj;
}
You have a bit of a tradeoff here. As you seem to already understand, methods you put on the .prototype are publicly available, but that is the most efficient places to put methods as they are automatically added to all new copies of that object in a very efficient manner. When using .prototype for methods, there is only one copy of your methods and a reference to that single copy is automatically added to all new instantiations of that object.
But, javascript doesn't have private methods built-in and the only work-around for that involves not using the .prototype for them or for any methods that need to call the private methods.
This article by Doug Crockford is a pretty good description of how you can create privacy for either data or methods in any object.
In either case, I don't see any reason to avoid using the new keyword to create new objects. You can make either .prototype or private methods work with new.
But, if you want to achieve truly private methods, then you can't use .prototype for either the private methods or any methods that need to access them so you have to decide which is more important to you. There is no single correct answer because your need for privacy is situation-specific.
In my coding, I generally don't enforce privacy and I do use .prototype and new. I designate "non-public" methods on the prototype by starting their name with an underscore. This is a notational convention, not an access enforcement scheme.
In answer to your second question about avoiding the new operator and reinstantiating methods, I'd just ask why you're doing this? What are you gaining? I'm not aware of any downsides to using new. As best I understand your decision about whether to use .prototype vs. manually create/assign methods in your constructor should be about the need for private methods.
FYI, 15 objects is hardly going to create a significant difference in performance either way here. I would evaluate your need for true privacy and make your decision based on that. If you HAVE to enforce privacy, then go with the Crockford method for implementing private methods. If you don't HAVE to have true privacy, then use .prototype. I don't see a reason here to avoid using new in either case.
You can use a metaconstructor* pattern to get around this.
function defineCtor(metaCtor) {
var proto = new metaCtor();
var ctor = proto.hasOwnProperty('constructor') ?
proto.constructor : new Function();
return (ctor.prototype = proto).constructor = ctor;
}
Now you have a function that constructs constructors (or more accurately constructs prototypes and returns constructors).
var Widget = defineCtor(function() {
function doInternalStuff() {
// ...cant see me
}
// this function ends up on the prototype
this.getFoo = function() { return doInternalStuff(); };
});
// ...
var myWidget = new Widget();
Explanation
defineCtor takes a single anonymous function as a property. It invokes the function with new, creating an object. It assigns the object as the prototype property of a new constructor function (either an empty function, or the generated prototype object's own constructor property), and returns that function.
This provides a closure for your internal functions, addressing your question 1, and sets up the constructor/prototype pair for you, addressing question 2.
Comparison
Compare the defineCtor technique to the following two examples.
This example uses the prototype, and has problem 1: the internal stuff is not encapsulated.
function Widget(options) {
this.options = options;
}
Widget.prototype = {
getFoo: function() {
return doInternalStuff();
}
};
// How to encapsulate this?
function doInternalStuff() { /* ... */ }
This example sets up everything in a constructor, and has problem 2: each time it constructs an object, it instantiates new function objects for each property.
function Widget(options) {
this.options = options;
function doInternalStuff() { /* ... */ }
this.getFoo = function() {
return doInternalStuff();
};
}
This example uses the technique described above to provide encapsulation while still leveraging the prototype:
var Widget = defineCtor(function() {
// ^
// This function runs once, constructing the prototype.
// In here, `this` refers to the prototype.
// The real constructor.
this.constructor = function(options) {
// In function properties, `this` is an object instance
// with the outer `this` in its prototype chain.
this.options = options;
};
function doInternalStuff() { /* ... */ }
this.getFoo = function() { return doInternalStuff(); };
});
// ...
var myWidget = new Widget();
This approach has a few benefits, some more immediately obvious than others.
It provides encapsulation. You could do this by wrapping the first "comparison" example in an immediately invoked function, but this approach may be cleaner and more easily "enforced" in a team setting.
It's extensible. You can give your "metaconstructor" functions their own prototypes, with function properties like "extends", "mixin", etc. Then, inside the body of metaCtor, you can write things like this.extends(BaseWidget). The defineCtor API never needs to change for any of this to happen.
It "tricks" Google Closure Compiler, Eclipse, jsdoc, etc. into thinking you are defining the actual constructor function rather than a "meta function." This can be useful in certain situations (the code is "self-documented" in a way these tools understand).
* As far as I know, the word "metaconstructor" is completely made up.

Categories

Resources