Prototypes and libraries

A

Andrew Poulos

If I'm building a javascript library (an object) like so:

myLib = new Object():
myLib.myFunction = function() {
// blah
}

What happens if I want to add a prototype to myFunction? I'm converting
some code from this form:

MyFunction = function(){
// blah
}
MyFunction.prototype.myMethod = function() {
// blah
}

and I'm wondering what the implications are.


Andrew Poulos
 
M

Michael Winter

If I'm building a javascript library (an object) like so:

I assume this is related to your post last Wednesday regarding name
clashes.
myLib = new Object():
myLib.myFunction = function() {
// blah
}

I tend to find that:

var myLib = (function() {
return {
myFunction : function() {
},
myOtherFunction : function() {
}
};
})();

is better. This allows to use "global" variables, but keeping them within
the object:

var myLib = (function() {
var myPrivate = 0;

return {
myMethod : function() {
alert(++myPrivate);
}
};
})();

myLib.myMethod(); // alert 1
myLib.myMethod(); // 2
myLib.myMethod(); // 3

The initial return (assigned to myLib) could be anything. I used an object
literal above as that's what you'd probably use when creating a
library-like object, but it could also be function (a constructor
function, for example) or anything else that might be more useful.

If you do manage to use this form exclusively, you'd only add one global
variable. If that clashed, client software could just use a different
name, even if you want to call one of your own methods:

var myLib = (function() {
return {
myMethod1 : function() {
alert('myMethod1 called');
this.myMethod2();
},
myMethod2 : function() {
alert('myMethod2 called');
}
};
})();

myLib.myMethod1(); // myMethod1 called
// myMethod2 called

As long as myMethod1 is called through the library object (whatever it's
called), the this operator will refer to the library and myMethod2 would
be called as expected.
What happens if I want to add a prototype to myFunction?

Adding a prototype would only have any meaningful effect if myFunction was
a constructor.
I'm converting some code from this form:

MyFunction = function(){
// blah
}
MyFunction.prototype.myMethod = function() {
// blah
}

From that form into which form?
and I'm wondering what the implications are.

Your question seems exceedingly vague. Could you post an example of the
code before and after your changes. Preferably something non-trivial.

Mike
 
A

Andrew Poulos

Michael said:
I assume this is related to your post last Wednesday regarding name
clashes.



I tend to find that:

var myLib = (function() {
return {
myFunction : function() {
},
myOtherFunction : function() {
}
};
})();

is better. This allows to use "global" variables, but keeping them
within the object:

var myLib = (function() {
var myPrivate = 0;

return {
myMethod : function() {
alert(++myPrivate);
}
};
})();

myLib.myMethod(); // alert 1
myLib.myMethod(); // 2
myLib.myMethod(); // 3

The initial return (assigned to myLib) could be anything. I used an
object literal above as that's what you'd probably use when creating a
library-like object, but it could also be function (a constructor
function, for example) or anything else that might be more useful.

If you do manage to use this form exclusively, you'd only add one
global variable. If that clashed, client software could just use a
different name, even if you want to call one of your own methods:

var myLib = (function() {
return {
myMethod1 : function() {
alert('myMethod1 called');
this.myMethod2();
},
myMethod2 : function() {
alert('myMethod2 called');
}
};
})();

myLib.myMethod1(); // myMethod1 called
// myMethod2 called

As long as myMethod1 is called through the library object (whatever
it's called), the this operator will refer to the library and myMethod2
would be called as expected.



Adding a prototype would only have any meaningful effect if myFunction
was a constructor.



From that form into which form?



Your question seems exceedingly vague. Could you post an example of the
code before and after your changes. Preferably something non-trivial.

Mike

Thanks for your help.

Here's some sample code that I want to convert to a library type
approach. (Note the object "Obj" has been setup earlier). I'm getting
caught on the idea behind how blah.onlick = this.playSound; would
convert. There are other bugs in the code as it's a work in progress.

function MySound(sndUrl,num) {
this.sndURL = sndUrl;
var t = sndUrl.toLowerCase();
var s = t.lastIndexOf(".");
t = t.substr(s);
switch(t) {
case ".aif":
this.datatype = "audio/x-aiff";
break;
case ".mid":
this.datatype = "audio/mid";
break;
case ".mp3":
this.datatype = "audio/mpeg";
break;
case ".wav":
this.datatype = "audio/x-wav";
break;
case ".wma":
this.datatype = "application/x-mplayer2";
break;
}
this.load();
document.getElementById("play"+num).onclick = this.playSound;
document.getElementById("stop"+num).onclick = this.stopSound;
}

MySound.prototype.load = function() {
if (!document.getElementById("divAudio")) {
Obj.mediaDiv = document.createElement("div");
Obj.mediaDiv.setAttribute("id", "divAudio");
Obj.mediaDiv.style.position = "absolute";
Obj.mediaDiv.style.visibility = "hidden";
document.body.appendChild(Obj.mediaDiv);
} else {
Obj.mediaDiv = document.getElementById("divAudio");
}
if (Obj.isCompat) {
var mediaObj = document.createElement("object");
mphAddParam(mediaObj,"FileName",this.sndURL);
mphAddParam(mediaObj,"AutoStart","false");
Obj.mediaDiv.appendChild(mediaObj);

mediaObj.id = "objmedia" +
(Obj.mediaDiv.childNodes.length-1).toString();
mediaObj.classid = "CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95";
mediaObj.codeBase =
"http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,5,715";
}
}

MySound.prototype.playSound = function() {
// get the sound number
var tmp = parseInt( this.id.replace(/\D/gi,' '),10 );
var objID = "objmedia"+tmp;

if (Obj.isCompat) {
document.getElementById(objID).Play();
} else {
var c = document.getElementById(objID);
if (c) Obj.mediaDiv.removeChild(c);

var mediaObj = document.createElement("object");
mediaObj.id = "objmedia" + tmp.toString();
mediaObj.setAttribute("type",mphMain["snd"+tmp].datatype);
mediaObj.setAttribute("data",mphMain["snd"+tmp].sndURL);

mphAddParam(mediaObj,"AutoStart","true");

mphObj.mediaDiv.appendChild(mediaObj);
}
}

MySound.prototype.stopSound = function() {
var tmp = parseInt( this.id.replace(/\D/gi,' '),10 );
var objID = "objmedia"+tmp;

if (Obj.isIE) {
document.getElementById(objID).Stop();
} else {
var c = document.getElementById(objID);
if (c) Obj.mediaDiv.removeChild(c);
}
}


Andrew Poulos
 
M

Michael Winter

[snip]
Here's some sample code that I want to convert to a library type
approach.

I don't see any need to do anything to that code.

[snip]

Mike
 
A

Andrew Poulos

Michael said:
[snip]
Here's some sample code that I want to convert to a library type
approach.


I don't see any need to do anything to that code.

[snip]

That means I don't yet understand when a library type approach is
appropriate.

Thanks for the help.

Andrew Poulos
 
A

Andrew Poulos

[snip]
var myLib = (function() {
return {
myMethod1 : function() {
alert('myMethod1 called');
this.myMethod2();
},
myMethod2 : function() {
alert('myMethod2 called');
}
};
})();

myLib.myMethod1(); // myMethod1 called
// myMethod2 called

If I did choose to use the approach you've outlines above, is there a
way to add more methods to the library at some later stage (in the way a
constructor function can have prototypes added)?


Andrew Poulos
 
M

Michael Winter

[snip]
That means I don't yet understand when a library type approach is
appropriate.

The pattern I showed is mainly used for encapsulation. For example, when
you want to separate a public interface (a constructor or factory
function, or an object) from supporting code that client software
shouldn't have (or need) access to.

Mike
 
M

Michael Winter

[...] is there a way to add more methods to the library at some later
stage (in the way a constructor function can have prototypes added)?

If you're modifying the public interface, then yes, it's easy.

From outside the "library":

myLib.newMethod = function() {
/* ... */
};

If you're modifying the interface from inside, then it depends what's
modifying it.

Interface code:

var myLib = (function() {
return {
myMethod : function() {
this.newMethod = function() {
/* ... */
};
}
};
})();

Private code:

var myLib = (function() {
var _i;

function myFunction() {
_i.newMethod = function() {
/* ... */
};
}

return (_i = {
/* Interface */
});
})();


If you're attempting to modify private code within the "library", then
it's fairly difficult to do externally - you'd have to provide access to
it which would negate the point of hiding the code in the first place.

Hope that helps,
Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top