I have a singleton object that use another object (not singleton), to require some info to server:
var singleton = (function(){
/*_private properties*/
var myRequestManager = new RequestManager(params,
//callbacks
function(){
previewRender(response);
},
function(){
previewError();
}
);
/*_public methods*/
return{
/*make a request*/
previewRequest: function(request){
myRequestManager.require(request); //err:myRequestManager.require is not a func
},
previewRender: function(response){
//do something
},
previewError: function(){
//manage error
}
};
}());
This is the 'class' that make the request to the server
function RequestManager(params, success, error){
//create an ajax manager
this.param = params;
this._success = success; //callbacks
this._error = error;
}
RequestManager.prototype = {
require: function(text){
//make an ajax request
},
otherFunc: function(){
//do other things
}
}
The problem is that i can't call myRequestManager.require from inside singleton object. Firebug consolle says: "myRequestManager.require is not a function", but i don't understand where the problem is. Is there a better solution for implement this situation?
Singletons are used to create an instance of a class if it does not exist or else return the reference of the existing one. This means that singletons are created exactly once during the runtime of the application in the global scope. Based on this definition, singletons seem very similar to global variables.
The most important drawback of the singleton pattern is sacrificing transparency for convenience. Consider the earlier example. Over time, you lose track of the objects that access the user object and, more importantly, the objects that modify its properties.
The singleton design pattern solves problems by allowing it to: Ensure that a class only has one instance. Easily access the sole instance of a class. Control its instantiation.
If you've worked with ES6 modules, and if you didn't already know it, ES6 modules are singletons by default. Specifically, by combining modules and the const keyword, you can easily write singletons.
Your code's in the order you quoted it, isn't it? The singleton appears above the RequestManager
in the source?
If so, that's your problem. It's fairly subtle(!), but assuming your two bits of quoted code are in the order you've shown them, here's the order in which things happen (I'll explain it more below):
RequestManager
is defined.RequestManager
.RequestManager
prototype is replaced with a new one.Since the myRequestManager
instance was instantiated before the prototype was changed, it doesn't have the functions you defined on that (new) prototype. It continues to use the prototype object that was in place when it was instantiated.
You can fix this easily by re-ordering the code, or by adding properties to RequestManager
's prototype rather than replacing it, e.g.:
RequestManager.prototype.require = function(text){
//make an ajax request
};
RequestManager.prototype.otherFunc = function(){
//do other things
};
That works because you haven't replaced the prototype object, you've just added to it. myRequestManager
sees the additions because you've added them to the object it's using (rather that setting a new object on the constructor function's prototype
property).
Why this happens is a bit technical and I'll mostly defer to the spec. When the interpreter enters a new "execution context" (e.g., a function, or the global — e.g., page-level — context), the order in which it does things is not strict top-down source order, there are phases. One of the first phases is to instantiate all of the functions defined in the context; that happens before any step-by-step code is executed. Details in all their glory in sections 10.4.1 (global code), 10.4.3 (function code) and 10.5 (declaration bindings) in the spec, but basically, the functions are created before the first line of step-by-step code. :-)
This is easiest to see with an isolated test example:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
</style>
<script type='text/javascript'>
// Uses Thing1
var User1 = (function() {
var thing1 = new Thing1();
function useIt() {
alert(thing1.foo());
}
return useIt;
})();
// Uses Thing2
var User2 = (function() {
var thing2 = new Thing2();
function useIt() {
alert(thing2.foo());
}
return useIt;
})();
// Thing1 gets its prototype *replaced*
function Thing1() {
this.name = "Thing1";
}
Thing1.prototype = {
foo: function() {
return this.name;
}
};
// Thing2 gets its prototype *augmented*
function Thing2() {
this.name = "Thing2";
}
Thing2.prototype.foo = function() {
return this.name;
};
// Set up to use them
window.onload = function() {
document.getElementById('btnGo').onclick = go;
}
// Test!
function go() {
alert("About to use User1");
try
{
User1();
}
catch (e)
{
alert("Error with User1: " + (e.message ? e.message : String(e)));
}
alert("About to use User2");
try
{
User2();
}
catch (e)
{
alert("Error with User2: " + (e.message ? e.message : String(e)));
}
}
</script>
</head>
<body><div>
<div id='log'></div>
<input type='button' id='btnGo' value='Go'>
</div></body>
</html>
As you can see if you run it, User1
fails because the Thing1
instance it's using doesn't have a foo
property (because the prototype was replaced), but User2
works because the Thing2
instance it uses *does (because the prototype was augmented, not replaced).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With