What are the differences between these three patterns of "class" definitions in JavaScript? - javascript

Are there any important/subtle/significant differences under the hood when choosing to use one of these four patterns over the others? And, are there any differences between the them when "instantiated" via Object.create() vs the new operator?
1) The pattern that CoffeeScript uses when translating "class" definitions:
Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();
and
2) The pattern that Knockout seems to promote:
var DifferentAnimal = function(name){
var self = this;
self.name = name;
self.move = function(meters){
return alert(this.name + (" moved " + meters + "m."));
};
}
and
3) a similar, simple pattern I've often seen:
var DifferentAnimalWithClosure = function(name){
var name = name;
var move = function(meters){
};
return {name:name, move:move};
}
and
4) The pattern that Backbone promotes:
var OneMoreAnimal= ClassThatAlreadyExists.extend({
name:'',
move:function(){}
});
Update 1: Changed pattern #2 and added pattern #3 in response to Elias' response // minor formatting

Just to be clear: JS doesn't know of classes, just objects and custom, self-defined constructor functions, but that's besides the point.
To answer your question in short: yes, there are some small and even some fairly large differences between the various ways of creating a new object you're posting here.
CoffeeScript:
This is actually the most clear-cut and traditional way to create your own constructor, but it has been "optimized" in the sense that it's been ready set-up to use (optional) closure variables.
Basically, what this code does, is use an IIFE, to wrap both the constructor definition and the proptotype method assignments in their own, private scope, that returns a reference to the new constructor. It's just clean, simple JS, no different from what you might write yourself.
Knockout:
Now this threw me a little, because to me, at least, the snippet you provide looks either like part of a module pattern, or a power constructor. But since you're not using strict mode, omitting the new would still make for dangerous situations, and since the entire function goes trough the trouble of creating a new instance of DifferentAnimal, only to then construct a second object literal, assigning all properties of DifferentAnimal to that secondary object, I'd say you're missing something. Because, truth be told, omitting the last return {}; statement here, would probably make no difference at all. Plus: as you can see, you're declaring a method (move) in what is, in essence, a constructor. This means that every instance will be assigned its own function object move, rather then getting it from the prototype.
In short: have another close look at where you got this snippet from, and double-check if this is the full version, because if it is, I can only see arguments against this.
Using a variable, defined inside the constructor is simply: a closure, suppose your properties have a distinct initial state, determined by some arguments, passed to that constructor:
function MyConstructor(param)
{
var paramInit = param/2;//or something
this.p = paramInit;//this property can change later on, so:
this.reInit = function()
{//this method HAS to be inside constructor, every instance needs its own method
this.p = paramInit;//var paramInit can't, it's local to this scope
};
}
var foo = new MyConstructor(10);
console.log(foo.p);//5
foo.p = 'hi';
console.log(foo.p);//hi
foo.reInit();
console.log(foo.p);//5
console.log(foo.paramInit);//undefined, not available outside object: it's a pseudo-private property
That's all there is too it, really. When you see ppl using var that = this; or something, that's often to create a reference to the main object that is available anywhere, without having to deal with the headaches of this (what does this reference? What should the method do when applied to an object other than the one it was originally intended for? etcetera...)
Backbone:
Here, we're dealing with another case: extending objects (IE: using methods, properties of either an existing "class" (constructor) or a particular instance) is not the same as simply creating an object.
As you well know, JS objects can be assigned new properties at any given time. Those properties can be removed, too. Sometimes, prototype properties can be redefined on the instance itself (masking the prototypal behaviour) etc... So it all depends on what you want the resulting object (the newly created object, that extends the given instance) to look like: do you want it to take all properties from the instance, or do you want both objects to use the same prototype somewhere down the line?
Both of these things can be achieved by using simple JS, too, but they just take a bit more effort to write yourself. However, if you write, for example:
function Animal(name)
{
this.name = name;
}
Animal.prototype.eat= function()
{
console.log(this.name + ' is eating');
};
That could be deemed the equivalent of writing:
var Animal = Object.extend({name:'',eat:function()
{
console.log(this.name + ' is eating');
}});
A lot shorter, but lacking the constructor.
new vs Object.create
Well, that's an easy one: Object.create just is a lot more powerful that new: you can define prototype methods, properties (including weather or not they are enumerable, writeable etc...) right at the time you need to create an object, instead of having to write a constructor and a prototype, or create an object literal and mess around with all those Object.defineProperty lines.
The downsides: Some people still aren't using ECMA5 compliant browsers (IE8 is still not quite dead). In my experience: it does become quite hard to debug sizeable scripts after a while: though I tend to use power-constructors more than I do regular constructors, I still have them defined at the very top of my script, with distinct, clear and quite descriptive names, whereas object-literals are things I just create "on-the-fly". Using Object.create, I noticed I tend to create objects that are really a little too complex to qualify as actual object literals, as though they are object literals:
//fictional example, old:
var createSomething = (function()
{
var internalMethod = function()
{//method for new object
console.log(this.myProperty || '');
};
return function(basedOn)
{
var prop, returnVal= {};
returnVal.myProperty = new Date();
returnVal.getCreated = internalMethod;//<--shared by all instances, thx to closure
if (!basedOn || !(basedOn instanceof Object))
{//no argument, or argument is not an object:
return returnVal;
}
for (prop in basedOn)
{//extend instance, passed as argument
if (basedOn.hasOwnProperty(prop) && prop !== '_extends')
{
returnVal[prop] = basedOn[prop];
}
}
returnVal._extends = basedOn;//<-- ref as sort-of-prototype
return returnVal;
};
}());
Now this is pretty verbose, but I've got my basic constructor ready, and I can use it to extend an existing instance, too. It might seem less verbose to simply write:
var createSomething = Object.create(someObject, {getCreated:function()
{
console.log(this.myProperty);
},
myProperty:new Date()});
But IMO, this makes it harder on you do keep track of what object is created where (mainly because Object.create is an expression, and will not be hoisted.Ah well, that's far from a conclusive argument of course: both have their pro's and con's: I prefer using module patters, closures and power constructors, if you don't that's just fine.
Hope this cleared up a thing or 2 for you.

The first example puts the move function in the prototype which will be shared between all Animal instances.
The second example creates a new move function for every the animal instance.
The third example generates a Animal class with the move function in the prototype similar to the first example but with allot less code.
(In your example the name is also shared between all instances, which you probably don't want)
Putting the function in the prototype makes instantiating Animals faster, and because of the way JIT engines work even the execution of the function is faster.

Related

private function return the last instance [duplicate]

Stylistically, I prefer this structure:
var Filter = function( category, value ){
this.category = category;
this.value = value;
// product is a JSON object
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
};
To this structure:
var Filter = function( category, value ){
this.category = category;
this.value = value;
};// var Filter = function(){...}
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
Functionally, are there any drawbacks to structuring my code this way? Will adding a prototypical method to a prototype object inside the constructor function's body (i.e. before the constructor function's expression statement closes) cause unexpected scoping issues?
I've used the first structure before with success, but I want to make sure I'm not setting myself for a debugging headache, or causing a fellow developer grief and aggravation due to bad coding practices.
Functionally, are there any drawbacks to structuring my code this way?
Will adding a prototypical method to a prototype object inside the
constructor function's body (i.e. before the constructor function's
expression statement closes) cause unexpected scoping issues?
Yes, there are drawbacks and unexpected scoping issues.
Assigning the prototype over and over to a locally defined function, both repeats that assignment and creates a new function object each time. The earlier assignments will be garbage collected since they are no longer referenced, but it's unnecessary work in both runtime execution of the constructor and in terms of garbage collection compared to the second code block.
There are unexpected scoping issues in some circumstances. See the Counter example at the end of my answer for an explicit example. If you refer to a local variable of the constructor from the prototype method, then your first example creates a potentially nasty bug in your code.
There are some other (more minor) differences. Your first scheme prohibits the use of the prototype outside the constructor as in:
Filter.prototype.checkProduct.apply(someFilterLikeObject, ...)
And, of course, if someone used:
Object.create(Filter.prototype)
without running the Filter constructor, that would also create a different result which is probably not as likely since it's reasonable to expect that something that uses the Filter prototype should run the Filter constructor in order to achieve expected results.
From a run-time performance point of view (performance of calling methods on the object), you would be better off with this:
var Filter = function( category, value ){
this.category = category;
this.value = value;
// product is a JSON object
this.checkProduct = function( product ){
// run some checks
return is_match;
}
};
There are some Javascript "experts" who claim that the memory savings of using the prototype is no longer needed (I watched a video lecture about that a few days ago) so it's time to start using the better performance of methods directly on the object rather than the prototype. I don't know if I'm ready to advocate that myself yet, but it was an interesting point to think about.
The biggest disadvantage of your first method I can think of is that it's really, really easy to make a nasty programming mistake. If you happen to think you can take advantage of the fact that the prototype method can now see local variables of the constructor, you will quickly shoot yourself in the foot as soon as you have more than one instance of your object. Imagine this circumstance:
var Counter = function(initialValue){
var value = initialValue;
// product is a JSON object
Counter.prototype.get = function() {
return value++;
}
};
var c1 = new Counter(0);
var c2 = new Counter(10);
console.log(c1.get()); // outputs 10, should output 0
Demonstration of the problem: http://jsfiddle.net/jfriend00/c7natr3d/
This is because, while it looks like the get method forms a closure and has access to the instance variables that are local variables of the constructor, it doesn't work that way in practice. Because all instances share the same prototype object, each new instance of the Counter object creates a new instance of the get function (which has access to the constructor local variables of the just created instance) and assigns it to the prototype, so now all instances have a get method that accesses the local variables of the constructor of the last instance created. It's a programming disaster as this is likely never what was intended and could easily be a head scratcher to figure out what went wrong and why.
While the other answers have focused on the things that are wrong with assigning to the prototype from inside the constructor, I'll focus on your first statement:
Stylistically, I prefer this structure
Probably you like the clean encapsulation that this notation offers - everything that belongs to the class is properly "scoped" to it by the {} block. (of course, the fallacy is that it is scoped to each run of the constructor function).
I suggest you take at the (revealing) module patterns that JavaScript offers. You get a much more explicit structure, standalone constructor declaration, class-scoped private variables, and everything properly encapsulated in a block:
var Filter = (function() {
function Filter(category, value) { // the constructor
this.category = category;
this.value = value;
}
// product is a JSON object
Filter.prototype.checkProduct = function(product) {
// run some checks
return is_match;
};
return Filter;
}());
The first example code kind of misses the purpose of the prototype. You will be recreating checkProduct method for each instance. While it will be defined only on the prototype, and will not consume memory for each instance, it will still take time.
If you wish to encapsulate the class you can check for the method's existence before stating the checkProduct method:
if(!Filter.prototype.checkProduct) {
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
}
There is one more thing you should consider. That anonymous function's closure now has access to all variables inside the constructor, so it might be tempting to access them, but that will lead you down a rabbit hole, as that function will only be privy to a single instance's closure. In your example it will be the last instance, and in my example it will be the first.
Biggest disadvantage of your code is closing possibility to override your methods.
If I write:
Filter.prototype.checkProduct = function( product ){
// run some checks
return different_result;
}
var a = new Filter(p1,p2);
a.checkProduct(product);
The result will be different than expected as original function will be called, not my.
In first example Filter prototype is not filled with functions until Filter is invoked at least once. What if somebody tries to inherit Filter prototypically? Using either nodejs'
function ExtendedFilter() {};
util.inherit(ExtendedFilter, Filter);
or Object.create:
function ExtendedFilter() {};
ExtendedFilter.prototype = Object.create(Filter.prototype);
always ends up with empty prototype in prototype chain if forgot or didn't know to invoke Filter first.
Just FYI, you cannot do this safely either:
function Constr(){
const privateVar = 'this var is private';
this.__proto__.getPrivateVar = function(){
return privateVar;
};
}
the reason is because Constr.prototype === this.__proto__, so you will have the same misbehavior.

Cannot combine dynamic prototype pattern with prototype chaining [duplicate]

Stylistically, I prefer this structure:
var Filter = function( category, value ){
this.category = category;
this.value = value;
// product is a JSON object
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
};
To this structure:
var Filter = function( category, value ){
this.category = category;
this.value = value;
};// var Filter = function(){...}
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
Functionally, are there any drawbacks to structuring my code this way? Will adding a prototypical method to a prototype object inside the constructor function's body (i.e. before the constructor function's expression statement closes) cause unexpected scoping issues?
I've used the first structure before with success, but I want to make sure I'm not setting myself for a debugging headache, or causing a fellow developer grief and aggravation due to bad coding practices.
Functionally, are there any drawbacks to structuring my code this way?
Will adding a prototypical method to a prototype object inside the
constructor function's body (i.e. before the constructor function's
expression statement closes) cause unexpected scoping issues?
Yes, there are drawbacks and unexpected scoping issues.
Assigning the prototype over and over to a locally defined function, both repeats that assignment and creates a new function object each time. The earlier assignments will be garbage collected since they are no longer referenced, but it's unnecessary work in both runtime execution of the constructor and in terms of garbage collection compared to the second code block.
There are unexpected scoping issues in some circumstances. See the Counter example at the end of my answer for an explicit example. If you refer to a local variable of the constructor from the prototype method, then your first example creates a potentially nasty bug in your code.
There are some other (more minor) differences. Your first scheme prohibits the use of the prototype outside the constructor as in:
Filter.prototype.checkProduct.apply(someFilterLikeObject, ...)
And, of course, if someone used:
Object.create(Filter.prototype)
without running the Filter constructor, that would also create a different result which is probably not as likely since it's reasonable to expect that something that uses the Filter prototype should run the Filter constructor in order to achieve expected results.
From a run-time performance point of view (performance of calling methods on the object), you would be better off with this:
var Filter = function( category, value ){
this.category = category;
this.value = value;
// product is a JSON object
this.checkProduct = function( product ){
// run some checks
return is_match;
}
};
There are some Javascript "experts" who claim that the memory savings of using the prototype is no longer needed (I watched a video lecture about that a few days ago) so it's time to start using the better performance of methods directly on the object rather than the prototype. I don't know if I'm ready to advocate that myself yet, but it was an interesting point to think about.
The biggest disadvantage of your first method I can think of is that it's really, really easy to make a nasty programming mistake. If you happen to think you can take advantage of the fact that the prototype method can now see local variables of the constructor, you will quickly shoot yourself in the foot as soon as you have more than one instance of your object. Imagine this circumstance:
var Counter = function(initialValue){
var value = initialValue;
// product is a JSON object
Counter.prototype.get = function() {
return value++;
}
};
var c1 = new Counter(0);
var c2 = new Counter(10);
console.log(c1.get()); // outputs 10, should output 0
Demonstration of the problem: http://jsfiddle.net/jfriend00/c7natr3d/
This is because, while it looks like the get method forms a closure and has access to the instance variables that are local variables of the constructor, it doesn't work that way in practice. Because all instances share the same prototype object, each new instance of the Counter object creates a new instance of the get function (which has access to the constructor local variables of the just created instance) and assigns it to the prototype, so now all instances have a get method that accesses the local variables of the constructor of the last instance created. It's a programming disaster as this is likely never what was intended and could easily be a head scratcher to figure out what went wrong and why.
While the other answers have focused on the things that are wrong with assigning to the prototype from inside the constructor, I'll focus on your first statement:
Stylistically, I prefer this structure
Probably you like the clean encapsulation that this notation offers - everything that belongs to the class is properly "scoped" to it by the {} block. (of course, the fallacy is that it is scoped to each run of the constructor function).
I suggest you take at the (revealing) module patterns that JavaScript offers. You get a much more explicit structure, standalone constructor declaration, class-scoped private variables, and everything properly encapsulated in a block:
var Filter = (function() {
function Filter(category, value) { // the constructor
this.category = category;
this.value = value;
}
// product is a JSON object
Filter.prototype.checkProduct = function(product) {
// run some checks
return is_match;
};
return Filter;
}());
The first example code kind of misses the purpose of the prototype. You will be recreating checkProduct method for each instance. While it will be defined only on the prototype, and will not consume memory for each instance, it will still take time.
If you wish to encapsulate the class you can check for the method's existence before stating the checkProduct method:
if(!Filter.prototype.checkProduct) {
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
}
There is one more thing you should consider. That anonymous function's closure now has access to all variables inside the constructor, so it might be tempting to access them, but that will lead you down a rabbit hole, as that function will only be privy to a single instance's closure. In your example it will be the last instance, and in my example it will be the first.
Biggest disadvantage of your code is closing possibility to override your methods.
If I write:
Filter.prototype.checkProduct = function( product ){
// run some checks
return different_result;
}
var a = new Filter(p1,p2);
a.checkProduct(product);
The result will be different than expected as original function will be called, not my.
In first example Filter prototype is not filled with functions until Filter is invoked at least once. What if somebody tries to inherit Filter prototypically? Using either nodejs'
function ExtendedFilter() {};
util.inherit(ExtendedFilter, Filter);
or Object.create:
function ExtendedFilter() {};
ExtendedFilter.prototype = Object.create(Filter.prototype);
always ends up with empty prototype in prototype chain if forgot or didn't know to invoke Filter first.
Just FYI, you cannot do this safely either:
function Constr(){
const privateVar = 'this var is private';
this.__proto__.getPrivateVar = function(){
return privateVar;
};
}
the reason is because Constr.prototype === this.__proto__, so you will have the same misbehavior.

Should properties ever be on the prototype?

Below I've got a simple little inheritance chain in JavaScript:
var Base =(function(){
function Base(config){
this.name = function(){
return config.name;
}
this.department = function(){
return config.department;
}
}
Base.prototype.calculate = function(){
return this.name().length + this.department().length;
}
return Base;
})();
var base = new Base({
name: 'me',
department: 'this one'
});
console.log(base.calculate());
//inheritance
var Concrete = (function(){
Concrete.prototype = Base.prototype;
function Concrete(config){
Base.apply(this,arguments);
this.number = function(){
return config.number;
}
}
return Concrete;
})();
var concrete = new Concrete({
name: 'concrete',
department: 'the best',
number: 3
});
console.log(concrete.number()); //3
console.log(concrete.name()); //concrete
and it works as expected. I'm curious, though about how correct it would be to place method properties on the prototype of an object. Canonically I know that instance data should go with the instance of the class and the methods the object actually uses should be on the prototype, but what about the situation where the properties themselves are methods (like name and department in Base in this example)? In the interest of keeping things immutable I'd rather not let users change the properties of one of these objects after they've been initialized. In this case does it make any sense to use the prototype instead of the constructor to add functionality to an object?
Or it is only correct to place properties in the constructor so you have access to them when you do something like Base.prototype.whatever?
what about the situation where the properties themselves are methods? In the interest of keeping things immutable I'd rather not let users change the properties of one of these objects after they've been initialized.
I wouldn't call these "properties" any more, but "accessor" or "getter" methods.
In this case does it make any sense to use the prototype instead of the constructor to add functionality to an object?
Yes, if you mean functionality like calculate. If you mean getters like name, department or number you need to place them in the constructor as they need privileged access to the config.
Canonically I know that instance data should go with the instance of the class and the methods the object actually uses should be on the prototype
It's a bit different actually:
Everything instance-specific (data, accessor methods with distinct scopes) needs to go on the instance and is usually set up in the constructor
Everything that is shared amongst all instances (typically methods, but also data in some rare cases) should go on the prototype.
Concrete.prototype = Base.prototype;
No! Use Concrete.prototype = Object.create(Base.prototype); instead.
The heading of your question is a bit misleading.
A direct answer to your heading - Yes, prototype properties are encouraged. They might not make a lot of difference wrt writing the code and the usage of the code, but internally, how JS manages things, like memory, accessing the variables inside closures etc it much better when done via prototype properties.
Now your actual question, is hiding immutable configurations using closures and private scoped variables the only way to do things ?
Right now - I think yes, as ES6 is still not supported completely by every browser yet.
But soon, the support will spread out in all release version of browsers and then you can use this nifty ES6 data type WeakMap . Just like a Map but here the keys are objects. So your Base definition can be written like:
var Base = (function() {
var properties = new WeakMap(); // You have to keep variable private
function Base(config) {
properties.set(this, config);
}
Base.prototype = {
name: function() {
return properties.get(this).name;
},
department: function() {
return properties.get(this).department;
},
calculate: function() {
return this.name().length + this.department().length;
}
};
return Base;
})();
Yes, you still need to keep the variable properties out of reach, so the outer closure. But the number of closures have decreased by 1 in this case.
Similar approach can be for ES6 Symbol but the outer closure will still be required.
What we truly need here are classes which will truly solve the issue, as explained in this very well written blog

Get the object that calls a function belonging to one of its properties OR namespacing prototypes?

(Obviously I'm not sure what the title of this should be.)
I'd like to be able to modify the prototypes of native objects with minimal risk (because trust me; I have heard over and over again how it's a mortal javascript sin).
My train of thought goes something like this: I can greatly reduce the possibility of conflicts if I can add my custom functions to a single object on a prototype rather than directly adding them to the prototype itself.
If I have the code,
String.prototype.foo = function() {
//do stuff
};
...obviously the keyword this references the string that called it. However, if I do something like,
String.prototype.foo = {};
String.prototype.foo.bar = function() {
//do stuff
};
...the keyword this references the object foo.
If there some way to overcome this?
The only way to overcome this would be to override the context using call:
String.prototype.foo.bar.call('hello'))
which works, but is pretty ridiculous.
You're better off (as you've already heard) avoiding modifying native prototypes, and instead having an object with your methods accepting the string as a parameter.
Well, there's a couple of ways to do this, depending on how much work you want to put in.
Using bind is the most straightforward way, but you have to define the string as a variable so you can pass a reference of it to bind:
String.prototype.foo = function() { console.log(this); }
String.prototype.foo.bar = function() { console.log(this); }
var g = "hi there";
g.foo() // Yields g
g.foo.bar() // Yields String.prototype.foo()
g.foo.bar.bind(g)() // Yields g again.
There may be another very hackish way to produce the desired result by creating getters and setters on String.prototype.foo so that String.prototype.foo.bar activates a function that returns a function bound to the instance that foo refers to. ??? Confusing.
What might be the best solution to reduce the possibility of conflicts, is to take advantage of prototypal inheritance and create your own sub prototype of the native String.
function MyString(string) {
this.foo = function() { //whatever };
this.foo.bar = (function() {}).bind(this);
this.toString = function() { return string; } // because toString is not generic.
}
MyString.prototype = new String();
Here, you're creating your own "sub-prototype" of the String prototype. It inherits all the properties of the String prototype and adds its own, all without altering the native strings at all. BONUS: this.foo and this.foo.bar will both refer to your instance.
var instance = new MyString("hi");
instance.foo(); // this == instance
instance.foo.bar(); // this == instance
instance.replace("i", "e"); // returns "he"
This maybe wasn't the answer you were looking for, but hopefully it will at least be helpful.

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