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

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!"));

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!"));

understanding simple class emulator in 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

Is there any benefit to using prototype instead of declaring properties on the object itself?

A prototype is used to declare properties and methods for a class of objects. One advantage of using prototype is that it conserves memory because all instances of a class point to the properties and methods of the prototype which conserves memory and effectively allows properties to be treated as static by all instances of a class.
Prototype is used for inheritance through prototype chaining.
My question is very simple. Why use prototype at all when you can just do:
function car() {
this.engine = "v8";
}
function mustang() {
// nm, no good way to inherit without using prototypes
}
Is that right? So the primary purpose of prototypes is threefold:
conserve memory
provide static properties
is the only way for a reference type to inherit from a super class
conserve memory
Yes it does, when you create hundreds of instances of Car and they all have their own functions (that have their own closure scopes) you'll consume more memory.
Can't find a reference for it but it has been suggested that Chrome optimises constructor functions that use prototype better than constructor functions with everything in the constructor body.
provide static properties
Static is more like Date.now(), every instance has members from the prototype but can be called on the instance.
is the only way for a reference type to inherit from a super class
You can inherit with Parent.apply(this,arguments); in Child but it makes extending Parent functions more complicated and doesn't make childInstance instanceof Parent true. What that code does is run Parent code with the to be created Child instance as the invoking object (this). Inheritance is usually done in 2 places.
In the Child body Parent.apply(this,arguments); to re use Parent initialisation code and make Parent instance members to be Child instance members (for example: this.name).
Setting Child.prototype to shallow copy of Parent.prototype Child.prototype=Object.create(Parent.prototype);Child.prototype.constructor=Child; This will ensure that shared Parent members are available on Child instances (like the function getName).
These points are explained in more detail here: https://stackoverflow.com/a/16063711/1641941
Concerning your three points:
Prototypes aren't necessarily more performant, especially for prototype chains that become long or contain many members. The smaller the prototype and shorter the chain, the more the browser's compiler can optimize it. Ultimately, this question needs to be asked for individual applications, their individual needs, and the browsers being targeted (which can vary widely in performance).
By definition, static members require objects. That is, static members belong to an object itself rather than to a specific instance. Objects are the only way to create static properties in JavaScript. Note that object literals, which are a "special" kind of Object, are essentially static.
One could implement his own type of object that would allow for something like inheritance (i.e., jQuery.extend), but as far as reference types go, prototypes are the only way to create inheritance.
Prototyping is a lot more than that.
You can also extend classes and already existing instances of objects with methods and properties during runtime.
This should explain it in a very understandable way: http://javascript.info/tutorial/inheritance
If you care about following conventions so that people (and you down the road) actually understand your code, you shouldn't put this.engine="v8" in the constructor. Prototype is meant to define properties for every single car, and the constructor is meant to define individual instances. So why would you put something that is true for every instance smack dab in the constructor? This belongs in the prototype. There's something to be said for putting things in their proper place even if doing both things end up doing accomplishing the same thing. Your code will be understandable by you and others.
Regarding your points:
There is definitely a performance boost, especially regarding functions - it's much better to declare functions on the prototype.
I think you meant to say "public" properties so that information is retrieved by writing some_instance.foo. "Static" properties/methods are different (see below).
Correct. Inheritance can only really happen from the prototype.
Let me explain some things to see if this helps. Creating new "classes" in javascript is a fairly straightforward process.
var MyClass = new Function();
At this point, the engine is aware of your new class and knows "what to do" (in terms of performance) when it creates new instances of your "class".
var my_instance = new MyClass();
If you want to modify the prototype, you can do so and know that every instance is going to get updated because they all share the same prototype.
MyClass.prototype.name = "default name";
console.log( my_instance.name ); //=> default name
Now the engine knows that there is a "name" property which expects a string value... it will allot those resources to all new AND existing instances of your class... which is very handy. Beware that modifying the prototype of an existing "class" like this is an expensive process and should not be performed frequently (but don't be afraid to do it either).
I can't really speak for the performance pros and cons of declaring ad-hoc properties/methods on an instance:
my_instance.foo = function() { /* this is not in the prototype chain */ };
My guess is that this is pretty simple for the engine and is no big deal unless you are doing this for tens of thousands of objects at the same time.
The main benefit of using the prototype IMO is that you can write code to extend a method's functionality and know that all instances of your "class" will get updated accordingly:
var old_foo = MyClass.prototype.foo;
MyClass.prototype.foo = function() {
/* new business logic here */
// now call the original method.
old_foo.apply(this, arguments);
};
Regarding "static" properties, you declare those on the "class" (constructor) itself:
// example static property
MyClass.num_instances = 0;
Now you can create init/destroy methods like this:
MyClass.prototype.init = function() {
this.constructor.num_instances++;
};
MyClass.prototype.destroy = function() {
this.constructor.num_instances--;
};
// and call the init method any time you create a new instance
my_instance.init();
console.log( MyClass.num_instances ); //=> 1
var instance_2 = new MyClass();
instance_2.init();
console.log( MyClass.num_instances ); //=> 2
instance_2.destroy();
console.log( MyClass.num_instances ); //=> 1
Hope that helps.
(1) I don't think conserving memory alone is a valid reason to utilize .prototype, unless you're getting really extreme with duplicating objects.
(2) The idea of static properties is not really a reason to use .prototype either (IMHO), because it doesn't behave like a traditional static property. You (as far as I know) always need an object instance before you can access the "static" property, which makes it not static at all.
function Car() {}
Car.prototype.Engine = "V8";
// I can't access Car.Engine... I'll always need an instance.
alert(new Car().Engine);
// or
var car1 = new Car();
alert(car1.Engine); //you always need an instance.
//unless you wanted to do
alert(Car.prototype.Engine); //this is more like a static property, but has an
//unintended consequence that every instance of Car also receives a .Engine
//behavior, so don't do this just to create a "static property."
It should be noted that this "static" idea not only applies to properties but all members, which includes methods (functions), from a traditional OO perspective.
It is better to think about prototypes (again, IMHO) as injected singleton objects with behaviors that get attached to instance objects. All instances of Car() can have their own instance members, but every instance of Car() will also "automatically" be injected with all Car.prototype's members/behaviors. It's not technically the same, but I find it convenient to think about prototypes in that way.
//define Car and Car.GoFast
function Car() {}
Car.prototype.GoFast = function () { alert('vroom!'); };
var car1 = new Car();
var car2 = new Car();
car1.GoFast();
car2.GoFast(); //both call to same GoFast implementation on Car.prototype
//change the GoFast implementation
Car.prototype.GoFast = function () { alert('vvvvvrrrrroooooooooommmmm!!!!!'); };
car1.GoFast();
car2.GoFast(); //both have been "updated" with the new implementation because
//both car1 and car2 are pointing to the same (singleton) Car.prototype object!
Car.prototype is behaving like a singleton object whose members/behaviors have been injected into instance objects of type Car.
(3) Prototypes shouldn't be confused for inheritance. You can get behavior that appears to be inheritance, but it is not. The members/behaviors on the prototype remain on the prototype object. They do not become members/behaviors of your derived class, like true inheritance. That is why I describe it more like the prototype being "injected" into your instance.
function Car() {}
Car.prototype.Engine = "V8";
var car1 = new Car();
var car2 = new Car();
alert(car1.Engine); //alerts "V8"
//There is no "Engine" variable defined within the instance scope of 'car1'.
//Javascript searches the scope of car1's Type.prototype object to find 'Engine'.
//Equivalent to: Object.getPrototypeOf(car1).Engine
//But if we create 'Engine' within the scope of car1
car1.Engine = "V6"; //Car.prototype was never accessed or updated
alert(car1.Engine); //we get "V6"
alert(car2.Engine); //we still get "V8"
alert(Object.getPrototypeOf(car1).Engine); //we still get "V8"!
So to answer the question directly: Is there any benefit to using prototype instead of declaring properties on the object itself?
Yes, when you want to share behavior implementation amongst instance objects of a given Type. As a coincidence, you will reduce your memory footprint, but that is not a reason alone to use prototypes. Neither is "creating static properties" (which they're not ), or for inheritance (which it is not).

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