Forum Moderators: open

Message Too Old, No Replies

Object literal versus prototype

Will using object literal as efficient as prototype if used in this manner?

         

DobbyM

2:46 pm on Feb 13, 2008 (gmt 0)

10+ Year Member



There's a discussion on this forum from 2006 which compares the relative memory footprints of instances of javascript classes, expressed using object literal versus object prototypes (http://www.webmasterworld.com/javascript/3066162.htm).

What I want to know is if this example, using prototype and the object literal...

function Obj(prop1, prop2) {
this.p1 = prop1;
this.p2 = prop2;
}

Obj.prototype = {
greet : function() {
alert(this.p1);
},
getP2 : function() {
return this.p2;
}
}

is as efficient as this example, which just uses prototype? Do instances of the first example create copies of the private functions, or references to them?

function Obj(prop1, prop2) {
this.p1 = prop1;
this.p2 = prop2;
}

Obj.prototype.greet = function(){
alert(this.p1);
}
Obj.prototype.getP2 = function(){
return this.p2;
}

Thanks

DobbyM

[edited by: DobbyM at 2:47 pm (utc) on Feb. 13, 2008]

fside

1:20 am on Feb 14, 2008 (gmt 0)

10+ Year Member



Javascript has a lot of different ways of doing the same thing. There doesn't seem to be much difference here.

I get confused about prototypes and linking, though. I'll readily admit that. So I'll be interested to see if anyone has an answer for you that, yes, there's a difference there.

My sense, again maybe wrong, is that logically there is a prototype for every object/instance, which prototype is a collection/table of labels/objects. Some are 'private', some are exposed to inferior objects. But a new object/instance doesn't copy the public objects of its constructing or superior object, certainly not the private ones. It can substitute for them. It can contain labels/objects of the same name and type that would override any superior of the same. But when created, I don't think there's anything in the new prototype. Calls to methods are made along a one-way up chain, until a null prototype is encountered. It is a first found on the way up inheritance. Again, someone correct me if that's wrong. One adds to each prototype to 'extend' that object's methods. But some don't like this idea of doing it this way. And the YUI-type frameworks seem more broken out by namespace than by object creation/inheritance (which means functions and objects tend to refer to each other by the apex, Yahoo, in this case).

mehh

4:13 pm on Feb 14, 2008 (gmt 0)

10+ Year Member



Some are 'private', some are exposed to inferior objects

I don't think that you can have private propertys in JavaScript. Equaly you can't inheret propertys from one "class" to another. The prototype object is the only method of inheretence. All propertys are exposed to all other objects.

DobbyM

4:31 pm on Feb 14, 2008 (gmt 0)

10+ Year Member



Of course, my mistake.

Can anyone answer the salient question from my original post: do instances of the example using prototype and the object literal create copies of the methods, or references to them?

[edited by: DobbyM at 4:33 pm (utc) on Feb. 14, 2008]

Fotiman

4:47 pm on Feb 14, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




Obj.prototype = {
greet : function() {
alert(this.p1);
},
getP2 : function() {
return this.p2;
}
}

vs.

Obj.prototype.greet = function(){
alert(this.p1);
}
Obj.prototype.getP2 = function(){
return this.p2;
}

Those are essentially the same thing.
All functions have an empty prototype object in case they are used as a constructor. So your second example is the equivalent of this:


Obj.prototype = {};
Obj.prototype.greet = function(){
alert(this.p1);
}
Obj.prototype.getP2 = function(){
return this.p2;
}

The end result is the same. You have a prototype object containing 2 functions.


Do instances of the first example create copies of the private functions, or references to them?

There would still only be a single prototype object for all instances of "Obj" because that's the way prototype inheritance works.
Hope that helps.

DobbyM

4:52 pm on Feb 14, 2008 (gmt 0)

10+ Year Member



Thanks, that's exactly what i was getting at.

fside

10:43 pm on Feb 14, 2008 (gmt 0)

10+ Year Member



All propertys are exposed to all other objects.

Functions not only are constructors, not only are objects, but have scope, and so a scope chain when using inheritance.

My confusion continues to be what the prototypes are supposed to include. Clearly one does not have to specifically use the "prototype" property. If you declare variables in the scope of a function, using it to construct other functions, those other functions can read that variable in the first, their constructor function. How would they execute a nested function in the constructor which was not assigned to a variable? or read its variables?

Fotiman

3:42 pm on Feb 15, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




My confusion continues to be what the prototypes are supposed to include.

If you have access to a copy of "JavaScript: The Definitive Guide", there's a great section in there on Prototypes and Inheritance.

Essentially, it's inefficient to assign methods to an object in the constructor (each instance of that class gets identical copies of those methods). Since methods don't typically change, there's no need for each instance to have a copy... it would be more efficient if there was only 1 copy of the methods and they all shared it.

The prototype object is associated with the constructor. Each instance of the class inherits the methods and constants defined in the prototype, without making a copy for each instance. So it's more efficient.


If you declare variables in the scope of a function,

Like this?

function foo() {
var x = 10;
}


using it to construct other functions,

Huh? I don't understand what you mean. Do you mean this:

function foo() {
var x = 10;
function bar() {
}
}


those other functions can read that variable in the first, their constructor function.

Yes, but I think your terminology is incorrect. A constructor function is used to create a new object. For example:
var f = new foo();

But I think this is where you were going with that:


function foo() {
var x = 10;
function bar() {
// I have access to x
alert("x = " + x);
}
bar();
}

In that example above, if I was to create a new foo() instance, the resulting object would have no public methods or variables, but each instance of foo would have a copy of a private var 'x', and a copy of the private method 'bar', which has access to foo's vars. Not efficient.


How would they execute a nested function in the constructor which was not assigned to a variable? or read its variables?

It could only be executed within the constructor itself, as in my example above.

Now, here's a slightly different approach. In this example, I'm creating 'bar' as a public method:


function foo() {
var x = 10;
this.bar = function() {
alert("x: " + x);
}
}
var f = new foo();
f.bar();

So now the foo class has a public method 'bar' that can be called, which still has access to foo's private var 'x'. However, each instance of foo still has a copy of the bar method. Still not efficient.

In this example, I've moved 'bar' to the prototype object, so each instance of foo will share this one copy of 'bar'. But unfortunately, 'bar' doesn't have access to foo's private vars like this, so this will fail:

function foo() {
var x = 10;
}
foo.prototype.bar = function() {
alert("x: " + x);
}
var f = new foo();
f.bar();

If, however, we make x a public property, then the prototype can get at it:


function foo() {
this.x = 10;
}
foo.prototype.bar = function() {
alert("x: " + this.x);
}
var f = new foo();
var g = new foo();
g.x = 20;
f.bar();
g.bar();

In that example, there is only 1 'bar' method instance (in the prototype object) and it is shared by both instances of foo.

Dustin Diaz has written a good explanation
of private, public, and privileged methods
[dustindiaz.com].

fside

6:16 pm on Feb 16, 2008 (gmt 0)

10+ Year Member



Dustin Diaz has written a good explanation

So I've been saying, and showing by example in other threads. But I have to disagree with him in this discussion of 'classes'. Javascript has a class, which is a property that tells the type of object. Otherwise, apart from primitives, javascript consists of objects, and prototype inheritance, not classes. Instead of specifically a class at the 'top', relying on instances for specifics, one instead simply modifies objects as one goes relying on first found upward inheritance, but through the prototype property and its collection. That's what confuses me.


unfortunately, 'bar' doesn't have access to foo's private vars like this, so this will fail:

Someone tried to correct me about that. But it's not so much private, as I understand it, but simply that these, along with the prototype property, are properties of the function object. Prototype's collection is just another property. These other properties, variables in this case, must be placed in the prototype collection property, or some reference made from within it (however it would be implemented).


each instance of foo would have a copy of a private [variables]

Wouldn't a new function object have a collection of its own, and its own prototype property. But wouldn't that prototype be empty, pointing only to the constructor's prototype. So these variables wouldn't be copied, since all the new function would be is just a collection with one member, its empty prototype object?


function fn() {
var x = 10;
this.b = function() {
alert("x: " + x);
}
}
var f = new fn();
f.b();

I found this also confusing. f now has only a prototype, an object Object, if you like, but nothing else. fn has a collection of x/value, and b/return value. Its prototype collection has a pointer to the b/return value, and an uplink to the constructor. The word, this, augments the prototype. As you say, an object using the keyword, this, is a public object, that is - part of the prototype collection. The constructor's own prototype collection isn't accessible to it. So there is no fn.b(). But, 'publicly', there will be an f.b().

So if one wishes to augment:


function func1() {
var x = 10;
this.y = function() { return typeof x; }
this.z = 5;
}

var fn = new func1();

func1.prototype.T = fn.y() + "3";

alert(fn.T);

Here both y and z are in the prototype collection. But only fn can 'see' this prototype, and so can access y() directly. So to extend func1, and so make a new variable, T, available to fn, one has to use fn's access to the prototype when augumenting the func1 constructor, itself, in this way?

Fotiman

5:03 pm on Feb 19, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




But I have to disagree with him in this discussion of 'classes'. Javascript has a class, which is a property that tells the type of object. Otherwise, apart from primitives, javascript consists of objects, and prototype inheritance, not classes.

I'm not sure what you mean by this. An object can be an instance of a particular class. For example:

var f = new foo();

f is an instance object of class foo.


Instead of specifically a class at the 'top', relying on instances for specifics, one instead simply modifies objects as one goes relying on first found upward inheritance, but through the prototype property and its collection. That's what confuses me.

Huh?
You're confusing me too. I don't know what you mean. As in my example above, the 'top' class is foo. Are you referring to polymorphism, whereby one class extends another?


Someone tried to correct me about that. But it's not so much private, as I understand it, but simply that these, along with the prototype property, are properties of the function object.

Essentially, it boils down to scope. In JavaScript, functions have scope. Therefore, variables declared in one function are not accessible to variables defined in another function (unless that second function is defined within that first function). So we say that those variables are 'private'. True, this is slightly different than object oriented languages like Java or C++, but the end result (for all practical purposes) is the same.

For example:


function foo() {
var x = 10;
}
// x is not defined outside of foo.
// Because x is not a "property" of foo
// we have no way to access it from outside
// of foo. It is only accessible within the
// scope of foo().
function bar() {
var y = 10;
function doThis() {
// doThis is defined within the scope
// of bar(), so therefore has access
// to y.
alert(y);
}
}
// Likewise, neither y or doThis are defined
// outside of bar(), and they are also not
// defined as properties of bar(), so therefore
// they can not be accessed outside of the scope
// of bar().
function acme() {
this.x = 10;
}
// acme now has a property 'x'. If we use acme()
// as a constructor, we create an instance of class
// acme, which will have property 'x'.
var a = new acme();
// We can now access the 'x' property of a
alert(a.x);


Prototype's collection is just another property.

Well, yes and no. It's more like a "read only" property. For example:


function Dog() {
}
Dog.prototype = {
legs : 4
}
var fido = new Dog();
var spot = new Dog();
alert('fido has ' + fido.legs + ' legs');
// Above, we read the legs property from the
// prototype object
fido.legs = 3;
// And here, we are creating a new legs property
// within the fido instance of class Dog. The
// next time we try to read the legs property
// it will find a value within the fido instance
// and therefore not access the prototype's value.
// However, spot will still get it's value from
// the prototype.



each instance of foo would have a copy of a private [variables]

Wouldn't a new function object have a collection of its own, and its own prototype property.


You keep using the term 'function object', which I think is a horrible choice of wording. If you have a function that's used as a constructor, then the objects that are created using that 'class' will each have their own copy of any properties of that class, and they will both share the same prototype object (they don't each have their own prototype).


But wouldn't that prototype be empty, pointing only to the constructor's prototype.

Huh? You lost me. What prototype are you talking about? I think I need some sort of visual representation of what you mean. In JavaScript, every *object* has a prototype. Therefore, regular functions that are not used as constructors will not have a prototype object that can be used in any meaningful way.


So these variables wouldn't be copied, since all the new function would be is just a collection with one member, its empty prototype object?

Again, I have no clue what you're talking about here.



function fn() {
var x = 10;
this.b = function() {
alert("x: " + x);
}
}
var f = new fn();
f.b();

I found this also confusing. f now has only a prototype, an object Object, if you like, but nothing else.

No, you're wrong.
f is an instance of fn. It has a public method b(), and a private variable x. f's prototype object is an empty object (the default) since we didn't specify one for fn.

fn has a collection of x/value, and b/return value.

fn is a constructor. It *has* nothing.

Its prototype collection has a pointer to the b/return value, and an uplink to the constructor.

No. It's prototype is an empty object. There is no pointer to b. And for objects created using fn as a constructor, the prototype will have a constructor property containing fn.

The word, this, augments the prototype.

No. 'this' refers to the instance object, not the prototype.

As you say, an object using the keyword, this, is a public object, that is - part of the prototype collection.

No. In JavaScript, a constructor passes a reference to the newly created object as the value of 'this', which is then used to initialize that object. It is *NOT* part of the prototype!

Here both y and z are in the prototype collection.

No, they are not.
You have created a property 'T' in the prototype object and you've assigned it a value (which is the result of calling your public method 'y' defined in the func1 constructor as a public method). The prototype does NOT have either y or z.

But only fn can 'see' this prototype, and so can access y() directly.

No. fn is an instance of func1, which has a public method 'y'. 'y' is NOT part of the prototype in this example.

So to extend func1, and so make a new variable, T, available to fn, one has to use fn's access to the prototype when augumenting the func1 constructor, itself, in this way?

You are creating a new property 'T' in the prototype for class func1. 'T' will be accessible to all instances of func1. You have augmented func1's prototype by creating an instance of func1 and then assigning a value to func1's prototype object. If you want to use private variables of func1 to manipulate the prototype for that class, then yes, you must create an instance first and access it that way.

fside

7:46 pm on Feb 20, 2008 (gmt 0)

10+ Year Member



var f = new foo();

f is an instance object of class foo.

Foo is an object of some particular type, called "class" in ECMA-262. Javascript doesn't present classes, but rather objects. Inheritance is had through objects. They inherit from the prototype property of their constructor object, called prototype-based inheritance.

And all constructor objects have a prototype. My confusion was that function instances were the end of the line, and could not again be used as constructors. A function's prototype is accessible to it by using the word, "prototype".

So


function func14(){
x = 15;
this.y = 20;
this.k = function(){ txt="y is " +y; alert(txt); return txt; }
func14.prototype.z2 = 50;
}

alert(typeof func14) > "function"
alert(func14.x) > "undefined"
alert(func14.y) > "undefined"
alert(func14.z2) > "undefined"
alert(func14.prototype.z2) > "undefined"

func14 collection (but not enumerable?):
name/value (key/context): x/15, y/20, k/<func literal>, prototype (empty), etc

func14();
alert(func14.prototype.z2) > 50

func14 prototype
name/value (key/context): z2/50

Then:

var func14sub = new func14;

alert(typeof func14sub) > "object"

func14sub collection:
name/value (key/context): y/20, k/<func literal>, prototype ( z2/50 ), etc

Copying, not inheritance. I was confused about that. The "y" and "k" are simply copied to func14's object instance, func14sub.

Or are you saying that "x" is copied to func14sub's collection, as well? If so, how would it be enumerated as were these others? (see below)

That object instance, func14sub, also cannot itself be used to construct other objects, as could func14. Its class is "object", not "function". The reserved word, instanceOf, shows func14sub is an instance object, of func14 (func14sub instanceOf func14). However, func14sub did inherit, by the prototype, "z2". Your wrong-wrong-wrong, was right. And I was wrong. I don't know if prototypes are becoming clearer to me, and your wrong-wrong-wrongs would be fewer. I never came at this from the class-inheritance side, as many obviously did. I have no problem simply seeing javascript as based on object-inheritance, not class-inheritance, and would not even use the word, class, though I'll admit it is freely used in ECMA documents.


regular functions that are not used as constructors will not have a prototype object that can be used in any meaningful way.

All functions have prototype collections (an object) as a property. Any function object could be used to construct instance objects.

ECMA: A prototype property is automatically created for every function, to allow for the possibility that the function will be used as a constructor.

Functions are created with the function constructor - new Function or function xx(). The prototype property can then be assigned to any function at all. So once func1Sub is created, func1Sub.prototype = func2 now looks to func2 for its inheritance. But "new func1", etc, even though "new" is being used with a function, is not the function constructor but the construct property of the function, and does not create a new function, but a new instance object instead, of type - "object".


f is an instance of fn. It has a public method b(), and a private variable x. f's prototype object is an empty object (the default) since we didn't specify one for fn.

It seemed confusing to me. I agree with you.


prototype will have a constructor property

It's a property of the constructor.

ECMA: A constructor is a Function object that creates and initialises objects. Each constructor has an associated prototype object that is used to implement inheritance and shared properties. . . . The constructor’s associated prototype can be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype.

So it's a bit circular. But you say that the prototype has a property, constructor, which obviously is not the same as constructor.prototype. So it might read - constructor.prototype.constructor?

I came across this as a way to enumerate what was in the collections:


if (!Array.forEach) { // mozilla already supports this
Array.forEach = function(array, block, context) {
for (var i = 0; i < array.length; i++) {
block.call(context, array[i], i, array);
}
};
}
Function.prototype.forEach = function(object, block, context) {
for (var key in object) {
if (typeof this.prototype[key] == "undefined") {
block.call(context, object[key], key, object);
}
}
};
String.forEach = function(string, block, context) {
Array.forEach(string.split(""), function(chr, index) {
block.call(context, chr, index, string);
});
};
var forEach = function(object, block, context) {
if (object) {
var resolve = Object; // default
if (object instanceof Function) {
// functions have a "length" property
resolve = Function;
} else if (object.forEach instanceof Function) {
// the object implements a custom forEach method so use that
object.forEach(block, context);
return;
} else if (typeof object == "string") {
// the object is a string
resolve = String;
} else if (typeof object.length == "number") {
// the object is array-like
resolve = Array;
}
resolve.forEach(object, block, context);
}
};
function alertx(context, objectkey, key, object){
alert(context +" : " +objectkey +" : " +key +" : " +object);
}

forEach( <name> or <name.prototype> etc , alertx)

I believe it's just for FireFox/Mozilla that support this forEach.

fside

8:01 pm on Feb 20, 2008 (gmt 0)

10+ Year Member



The "if (!Array.forEach)" conditional covers IE, too. So it works there as well.

And in "name/value (key/context): y/20, k/<func literal>, prototype ( z2/50 ), etc", I meant that prototype was func14's prototype, showing z2/50 when looking at func14Sub. Is the private var, "x", also part of func14Sub's collection/dictionary, what have you?

fside

5:33 am on Feb 21, 2008 (gmt 0)

10+ Year Member



Followup again. Your prototype.constructor was also right. I've never needed to use it. But then I've never had to use prototyping, obviously. ECMA was confusing. Yes, it is indeed obj.prototype.constructor.

So:


Func1(){
Func1.prototype.func2 = function(){}
}

Fn.prototype = new Func1();
Fn.prototype.constructor = Fn;
Fn.prototype.baseClass = Fn.prototype.constructor;

function Fn(){}

var fn = new Fn();

fn.func2();