Javascript singleton: How to access private variables? - javascript

I have this singleton. The public functions allow to set private variables but I am not able to set them in the way that I am able to use them in the private methods:
var ServiceInterface = (function () {
// Instance stores a reference to the Singleton
var instance;
function init() {
// Singleton
// Private methods and variables
var m_action;
function performAction() {
alert(m_action);
}
return {
// Public methods and variables
callBackend: function (sAction) {
m_action = sAction;
}
};
};
})
m_action is not available throughout the public and private section of the singleton. What am I doing wrong?

I think you're referring to this
var ServiceInterface = (function () {
var m_action;
function performAction() {
alert(m_action);
}
return {
// Public methods and variables
callBackend: function (sAction) {
m_action = sAction;
performAction();
}
};
})()
ServiceInterface.callBackend("Hello world");
You need to execute anonymous function. Running it enables to create variables and functions defined with the anonymous function and these cannot be seen outside. Most of js libraries use this convention to create modules and to avoid infecting the global scope
var ServiceInterface = (function () {
// code inside
})()
These are the variables and functions confined within the scope of anonymous function executed
// var ServiceInterface = (function () {
var m_action;
function performAction() {
alert(m_action);
}
// })()
Finally, return a javascript object that will expose functions that can be accessible outside the anonymous function scope
//var ServiceInterface = (function () {
// var m_action;
//
// function performAction() {
// alert(m_action);
// }
return {
// Public methods and variables
callBackend: function (sAction) {
m_action = sAction;
performAction();
}
};
//})()
However, why go through the trouble of making private variable and method for a singleton?

This is how you create a singleton with a private member variable.
ServiceInterface = new (function() {
var m_action = false;
this.setAction = function(s) {
m_action = s;
};
this.getAction = function() {
return m_action;
};
this.performAction = function() {
alert(ServiceInterface.getAction());
};
this.createCallback = function() {
return function(sAction) {
ServiceInterface.setAction(sAction);
};
}
})();
ServiceInterface.setAction("secret");
ServiceInterface.performAction();
ServiceInterface becomes a singleton because the constructor function is thrown away after it's created. ServiceInterface = new (.....)(); is how it's executed right after being declared. It's a singleton because there is no way for someone to create another instance.
I don't use var to create the instance. When you exclude var and you're not inside a function the new variable will be attached to the prototype of the parent object. In the browser this will be window. window is like a global singleton in javascript.
The local variable m_action is persisted because setAction and getAction reference it as closure functions, and the variable is attached to their scope. So they can be used as setter/getter methods.
You can now use ServiceInterface.setAction(sAction); in your callbacks to set the private member.
http://jsfiddle.net/thinkingmedia/w7DdE/6/

Related

Private Variables in Module Pattern

I am not clear about the concept of private, if I can still access it through the public method and redefine the properties of the module. I mean, I can perfectly do:
var aModule = (function() {
var privateVar = 1;
return {
publicFunction: function() {
return privateVar;
}
}
})();
aModule.publicFunction = function() {
privateVar = function() {
console.log('a new content');
}
privateVar();
};
aModule.publicFunction(); // 'a new content'
I understand that this is not possible if I write it in ES6 with let or const, because it would give me error try to overwrite the value of the private variable, but what sense does it have in ES5?
A private variable cannot be accessed or changed by code that's outside the module or class that owns the private variable.
For example, you can't do aModule.privateVar and expect it to give you anything back.
What your publicFunction is is what the Java (and other programming languages) world would call a "getter". Simply put it gives access to the value of the private variable, without allowing write access to it.
In your last example, you're not actually overwriting the private variable. You're just creating a new variable within publicFunction's scope and assigning a value to that. Just because it's also named privateVar doesn't mean it's the same area of memory.
I've added to your code to demonstrate this
var aModule = (function() {
var privateVar = 1;
return {
publicFunction: function() {
return privateVar;
},
getPrivateVar() {
return privateVar;
}
}
})();
aModule.publicFunction = function() {
privateVar = function() {
console.log('a new content');
}
privateVar();
};
aModule.publicFunction(); // 'a new content'
console.log(aModule.getPrivateVar()); //outputs 1
To give more detail on why this is, it's all about scope. privateVar exists in an anonymous function's scope. This anonymous function returns an object with several functions defined on it. When the function is called, this object is assigned to aModule, but retains access to privateVar because they share scope.
However, outside of that function we're at a different scope, which doesn't have access to the aModule's variables, except those exposed in the returned object
You are overwriting the public function and not able to access the module private variable
Consider the following new function that is created as a property of aModule that attempts to only change the value of var privateVar
The scope is different because of where it gets called and it isn't able to access that private variable
var aModule = (function() {
var privateVar = 1;
return {
publicFunction: function() {
return privateVar;
}
}
})();
aModule.newFunction = function() {
// try to change privateVar in aModule
privateVar = 3
};
aModule.newFunction();
console.log(aModule.publicFunction()); //still 1 not 3
// here's where it actually ended up
console.log(window.privateVar)

Javascript varibles across functions

As an exercise, I'm writing my code within the same namespace (namespace being sample):
if ("undefined" == typeof(sample)) {
var sample = {};
}
sample = {
foo : function () {
},
bar : function () {
}
};
How do I create a variable within the same namespace which is accessible by all functions?
I assume you're trying to access a variable inside the sample namespace. To do this, you declare the variable at the same level as your functions, and from within the functions, you access it using the keyword this
sample = {
myVar:10; // this is your variable
foo : function () {
return this.myVar++; // use this keyword to access it.
},
bar : function () {
}
};
Note: If you're creating a function (closure) inside your function, you'll want to create an alias for the this keyword (like _self) so that the context isn't overridden inside your new function.
Another alternative:
(function(){
//Constructor
function Sample(name) {
this.sampleName = name;
}
Sample.prototype = {
foo: function() {
this.sampleName = "otherSample";
},
bar: function() {
this.sampleName += "anotherSample";
}
};
var sample = new Sample("wow");
})();

Why is this instance known to the javascript function

How can it be that the sampleViewModel instance in the addChangedValue function is not undefined or null ?
From the scope of instantiation the sampleViewModel should not be known in the addChangedValue function as far my understanding is.
$(function(){
var PersonViewModel = function() {
var me = this;
me.firstName = ko.observable('Lisa'),
me.lastName = ko.observable('T'),
me.changes = ko.observableArray()
};
var sampleViewModel = new PersonViewModel();
sampleViewModel.firstName.subscribe(function(newValue) {
addChangedValue("firstname refreshed: " + newValue);
});
function addChangedValue(updatedValue) {
sampleViewModel.changes.push({ value: updatedValue });
};
ko.applyBindings(sampleViewModel, document.getElementById('data'));
});
It most certainly would. Closures are lexically-bound to the scope in which they are defined, and so anything that was defined in the scope, in which the closure was also defined, will be known to that closure.
Also, the addChangedValue will have access to any variable in its enclosing scope unless you shadow it by defining a similarly-named variable within that function.
The anonymous function that is an argument to addChangedValue forms a closure. The sampleViewModel is the same variable within the function as it is in the enclosing scope. A closure is a combination of a function (often anonymous, as in your case) with a context. All variables in scope when the function is declared are available within that function. You can read about closures here.
Yes, you've created a Closure. By example:
function init() {
var person = { name: 'Joe' };
var callMeLater = function() {
alert(person.name);
};
return callMeLater;
};
var fn = init();
fn(); // alerts 'Joe'
In this example, the init() function has its own scope - which doesn't seem to be new to you. So for example person is declared inside of it, and therefore local to init(). callMeLater is defined inside this function, creating a Closure. All variables local to the enclosing function (init) are also available to callMeLater. And that's why the last line in this example alerts Joe, and your example makes sampleViewModel available.
It is as though in Java you did this:
public class Person {
public string name;
public Person(string name) {
this.name = name;
}
public MyClosure getClosure() {
string someOtherThing = "other";
return new MyClosure(this, someOtherThing);
}
}
public class MyClosure {
public Person person;
public string someOtherThing;
public MyClosure(Person person, string someOtherThing) {
this.person = person;
this.someOtherThing = someOtherThing;
}
public void callMeLater() {
log(person.name);
}
}
var person = new Person("Joe");
var closure = person.getClosure();
closure.callMeLater(); // Logs "Joe"
You can think of the MyClosure class as being an implicit wrapper created for you by defining a function inside a function.

Way to make global private variables in javascript?

Is there a way to create private global variables in JavaScript? I have tried looking around, and I keep bumping into talk of constructors - which don't seem too global.
Thanks
Not sure what your use case is. I'll assume you have a js script file containing some functions and variables and you want to expose some of those globally, but keep the rest private to your script file. You can achieve this with a closure. Basically you create a function that you execute immediately. Inside the function you place your original code. You then export the functions you need into the global scope.
// Define a function, evaluate it inside of parenthesis
// and execute immediately.
(function(export) {
var myPrivateVariable = 10;
function myPrivateFunction(param) {
return param + myPrivateVariable;
}
export.myGlobalFunction = function(someNumber) {
return myPrivateFunction(someNumber);
};
})(this); // The *this* keyword points to *window* which
// is *the* global scope (global object) in a web browser
// Here it is a parameter - the *export* variable inside the function.
// This is executed in the global scope
myGlobalFunction(2); // yields 12 (i.e. 2 + 10)
myPrivateVariable; // Error, doesn't exist in the global scope
myPrivateFunction(2) // Error, doesn't exist in the global scope
To answer your question, no, that is not possible as there are no access modifiers in javascript. A variable declared in global scope is accessible to any function.
As pointed out in the comments to this answer, you can create objects which have private members. Crockford has a page on private members in Javascript. He uses the following code to illustrate his point:
function Container(param) {
// private method
function dec() {
if (secret > 0) {
secret -= 1;
return true;
} else {
return false;
}
}
this.member = param;
var secret = 3;
var that = this;
// privileged method
this.service = function () {
return dec() ? that.member : null;
};
}
In the above example, param, secret, and that are all private in that they are not accessible from the outside. To be more clear, these variables can only be accessed by privileged or private methods, the difference being that privileged methods can be called from any instance of the object. As is suggested in the comments, this is achievable by using closures.
Quoting from Crockford for a quick explanation on closures, but you can find plenty of related questions.
What this means is that an inner function always has access to the
vars and parameters of its outer function, even after the outer
function has returned.
in order to have private members. you need to use closures.
following code help you understanding the concept.
function CustomArray () {
this.array = [];
var privateData = 'default data';
this.getPrivateData = function () {
return privateData;
};
this.setPrivateData = function (data) {
privateData = data;
};
};
CustomArray.prototype.push = function (data) {
this.array.push(data);
};
CustomArray.prototype.unshift = function (data) {
this.array.unshift(data);
};
CustomArray.prototype.pop = function () {
this.array.pop();
};
CustomArray.prototype.shift = function () {
this.array.shift();
};
CustomArray.prototype.print = function () {
console.log(this.array.join(','));
};
var array = new CustomArray();
array.push(10);
array.push(20);
array.push(30);
array.push(5);
array.unshift(3);
array.unshift(2);
array.unshift(1);
array.unshift(0);
array.pop();
array.shift();
array.print();
console.log(array.getPrivateData());// default data
array.setPrivateData('am new private data');
console.log(array.getPrivateData());//am new private data

Is it possible to pass execution context of the immediately invoked function expression

Consider the following code:
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
})();
window.myGlobalObj = {
init: function() {
// and somehow here I want to access to the IIFE context
}
};
I want to have the execution context of IIFE in my global object. I do have access to function expression and object itself so I can pass or modify something to make it work (and no, I can't rewrite everything inside the object or function).
Is it even possible?
The only way I see how that's poosible is by using eval to simulate dynamic scopes. Do this (note that the IIFE must be placed after the global object):
window.myGlobalObj = {
init: function() {
// and somehow here I want to access to the IIFE context
}
};
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
eval("(" + String(window.myGlobalObj.init) + ")").call(window.myGlobalObj);
})();
Here's a reference as on how to use dynamic scopes: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
Edit: I've included an example to demonstrate the power of using dynamic scopes in JavaScript. You can play with the fiddle too.
var o = {
init: function () {
alert(a + b === this.x); // alerts true
},
x: 5
};
(function () {
var a = 2;
var b = 3;
eval("(" + String(o.init) + ")").call(o);
}());
The "contents" of your IIFE, i.e., a, someFunc, etc., are local to that function scope, so you can only access them within that scope. But you can assign window.myGlobalObj inside the IIFE:
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
window.myGlobalObj = {
init: function() {
// and somehow here I want to access to the IIFE context
}
};
})();
Then the init function will have access to those variables since they are in its containing scope.
EDIT: if you can't move the definition of myGlobalObj into the IIFE the only thing I can think of is to use the IIFE to create a second global object that you access from myGlobalObj:
(function() {
var a = 5;
var someFunc = function() { ... };
function anotherFunc() {
...
};
// create a global object that reveals only the parts that you want
// to be public
window.mySecondObject = {
someFunc : someFunc,
anotherFunc : anotherFunc
};
})();
window.myGlobalObj = {
init: function() {
window.mySecondObject.someFunc();
}
};
No. It is not possible. The context you want to access is called closure and can be accessed only within the function (in your case, the anonymous function (IIFE how you call it)). For more about closures follow the excellent Douglas Crockfords The Javascript programming language video tutorial.
You will have to place those attributes to some shared object.

Categories