This is a very old problem, but I cannot seem to get my head around the other solutions presented here.
I have an object
function ObjA() {
var a = 1;
this.methodA = function() {
alert(a);
}
}
which is instantiated like
var myObjA = new ObjA();
Later on, I assign my methodA as a handler function in an external Javascript Framework, which invokes it using the apply(...) method.
When the external framework executes my methodA, this belongs to the framework function invoking my method.
Since I cannot change how my method is called, how do I regain access to the private variable a?
My research tells me, that closures might be what I'm looking for.
You already have a closure. When methodA is called the access to a will work fine.
Object properties are a different thing to scopes. You're using scopes to implement something that behaves a bit like ‘private members’ in other languages, but a is a local variable in the parent scope, and not a member of myObjA (private or otherwise). Having a function like methodA retain access to the variables in its parent scope is what a ‘closure’ means.
Which scopes you can access is fixed: you can always access variables in your parent scopes however you're called back, and you can't call a function with different scopes to those it had when it was defined.
Since a is not a property of this, it doesn't matter that this is not preserved when calling you back. If you do need to get the correct this then yes, you will need some more work, either using another closure over myObjA itself:
onclick= function() { myObjA.methodA(); };
or using Function#bind:
onclick= myObjA.methodA.bind(myObjA);
yes, you're right. Instead of a method reference
var myObjA = new ObjA();
libraryCallback = myObjA.methodA
pass a closure
libraryCallback = function() { myObjA.methodA() }
If you are using jQuery javascript framework, easiest way is to use proxy:
$('a').click($.proxy(myObjA, 'methodA'));
I'd do this:
function ObjA() {
this.a = 1;
this.methodA = function() {
alert(this.a);
}
}
function bindMethod(f, o) {
return function(){
return f.apply(o, arguments);
}
}
var myObjA = new ObjA();
myObjA.methodA = bindMethod(myObjA.methodA, myObjA);
...
Where bindMethod binds the methodA method to always be a method of myObjA while still passing on any arguments which function() {myObjA.methodA()} doesn't do.
Related
In traditional OOP language, we usually use private/public to implement data encapsulation.
In Javascript, there is no private or public anymore; someone told me; by using closure, the data encapsulation can be implemented. I am wondering how and what's the behind logic?
You can encapsulate data in a 'Class' (no real class before JavaScript 6) this way
var yourClass = function() {
var privateProp = 'sometext'; //private prop
this.prop = 1; //public
this.getPrivateProp = function() {
return privateProp; //access to your private prop with a closure
}
}
var test = new yourClass();
//when you use 'new', everything in 'this' is returned in your object.
//'privateProp' is not in 'this' but 'getPrivateProp' is.
//You got your private data not directly accessible from outside.
test.prop; // 1
test.privateProp;//undefined
test.getPrivateProp();// 'sometext'
Actually isn't creating actual private members.
Check the following code:
function A() {
var doStuff1 = function() { };
this.doStuff2 = function() {
doStuff1();
};
};
var instance = new A();
instance.doStuff2();
Since doStuff2 is declared and added to this, it's part of A instance while doStuff1 is declared as a local variable within the constructor function, and thus, it's only accessible using closures within the same constructor.
BTW I don't like this pattern since it works great when you don't use prototypal inheritance.
Let's say I want to use prototypes:
function A() {
var doStuff1 = function() {}; // ??????
};
A.prototype = {
doStuff2: function() {
// How do I access a local variable defined
// in the constructor function local scope?
}
};
So, the whole pattern works in simple scenarios where you don't want to use prototypal inheritance.
Also, this pattern won't work in scenarios where you want to use Object.create(...), since there's no constructor function at all...
// Constructor isn't ever called...
var instance = Object.create(A.prototype);
So, how you would implement this kind of encapsulation in JavaScript? For now isn't possible, but many libraries and frameworks have opted-in to use naming conventions to let developers know what's consumed by the library/framework code and what's intended for use in actual third-party developments.
For example:
function A() {
};
A.prototype = {
___doStuff1___: function() {},
doStuff2: function() {
this.___doStuff1___();
}
};
After all, this is a naming convention, where members which are sorrounded by ___ are considered private or not intended for third-party developers.
Other libraries/framework use $$ (f.e. Angular, $$privateMember).
My application is accessing a third party external JavaScript file that I cannot alter.
Within the file is an object defined similarly to as follows:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
}
})(window);
I need to be able access the object_1_var within the object, but I'm struggling to access it.
object_1.v // returns undefined
object_1.obj_1_func() // returns the value via console, but I need to assign it to a var.
I have tried extending the object using as follows: (Using jQuerys $.extend())
object_2 = (function(){
return {
obj_2_func: function() {
return object_1_var;
}
}
})(window);
$.extend(object_1, object_2);
var my_var = object_1.obj_2_func(); // returns 'Uncaught ReferenceError: object_1_var is not defined'
What can I do to be able to access object_1_var?
You will not be able to access the variable. It happens to be a private member. Private members of an object can be accessed only by its member functions.
Read this.
object_1_var is a lexically scoped local variable.
That means that it can't be accessed by extending object_1 outside of its original definition.
The only way it can be accessed is by adding functions within the original lexical scope in which it was declared:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
var_1: function(x) {
if (typeof x !== 'undefined') {
object_1_var = x;
} else {
return object_1_var;
}
}
}
})(window);
but since you can't modify object_1, you're out of luck, I'm afraid!
Make it public, like this:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
},
object_1_var: object_1_var
}
})(window);
EDIT
If unable to edit the javascript (such as in a third party library - sorry for omission) then you will not be able to have access to object_1_var as it's scope is local to the closure object_1.
What you are trying to accomplish is impossible in JS.
With the construction of object_1 the variable goes out of scope of that method. The reason why the logging function can access the variable is what we call 'a closure'.
Sadly, object_1_var isn't accessible in this example. The variable is defined as local to within that particular function - the only reason that the other functions can see it is because they are also defined within that function. This "closure scoping" is an interesting feature in JavaScript that you don't see very often elsewhere, but is the only real way of defining "private" variables in JavaScript Objects.
Hope that helps!
In a worst case scenario, in the past I've worked around this sort of issue by effectively overwriting the definition of an object that was previously defined elsewhere - mainly in Greasemonkey scripts - but I wouldn't condone this for production uses!
The trick here is to just copy the entire piece of script into your own. It's ugly as hell, but it might just work! (YMMV)
Hi I don't know whether this is my mistake in understanding Javascript prototype object ..
Well to be clear I'm new to the Javascript singleton concept and lack clear cut knowledge in that but going through some referral sites I made a sample code for my system but it's giving out some errors which I couldn't find why so I'm asking for your help. My code is:
referrelSystem = function(){
//Some code here
}();
Prototype function:
referrelSystem.prototype.postToFb = function(){
//Some Code here
};
I get an error saying prototype is undefined!
Excuse me i thought of this right now
EDIT
I have used like this:
referrelSystem = function(){
return{
login:getSignedIn,
initTwitter:initTw
}
};
Is this causing an issue?
A typical way to define a JavaScript class with prototypes would be:
function ReferrelSystem() {
// this is your constructor
// use this.foo = bar to assign properties
}
ReferrelSystem.prototype.postToFb = function () {
// this is a class method
};
You might have been confused with the self-executing function syntax (closures). That is used when you would like to have "private" members in your class. Anything you declare in this closure will only be visible within the closure itself:
var ReferrelSystem = (function () {
function doSomething() {
// this is a "private" function
// make sure you call it with doSomething.call(this)
// to be able to access class members
}
var cnt; // this is a "private" property
function RS() {
// this is your constructor
}
RS.prototype.postToFb = function () {
// this is a class method
};
return RS;
})();
I would recommend that you study common module patterns if you're looking into creating a library.
Update: Seeing your updated code, the return from referrelSystem won't work as expected, since return values are discarded when calling new referrelSystem().
Rather than returning an object, set those properties to this (the instance of referrelSystem that gets constructed):
var referrelSystem = function () {
// I assume you have other code here
this.login = getSignedIn;
this.initTwitter = initTw;
};
I don't think you intend to immediately execute the functions, change them to this:
var referrelSystem = function(){
//Some code here
};
(+var, -())
Same with the prototype function:
referrelSystem.prototype.postToFb = function(){
//Some Code here
};
(Here you don't need the var, because you're assigning to something that already exists.)
A function should return to work as
prototype
property.
Take a look at this example here
This question is best explained with some code, so here it is:
// a class
function a_class {
this.a_var = null;
this.a_function = a_class_a_function;
}
// a_class::a_function
function a_class_a_function() {
AFunctionThatTakesACallback(function() {
// How to access this.a_var?
});
}
// An instance
var instance = new a_class();
instance.a_function();
From within the callback in AFunctionThatTakesACallback(), how does one access this.a_var?
You'll need to expand the scope of this by creating a local variable that references it, like this:
function a_class_a_function() {
var self = this;
AFunctionThatTakesACallback(function() {
console.log(self.a_var);
});
}
The reason why you need to do this is because the this reference within the AFunctionThatTakesACallback function is not the same this as the current object, it will likely reference the global windowobject instead. (usually not what you want).
Oh, did I mention that this is called a closure?
You could try using the call method of function objects, which lets you specify a value for this:
myFunction.call(this, args...)
But I think that in this case it would probably be more straightforward to pass 'this' in as one of the parameters to the callback.
When you call instance.a_function(), you're really calling a_class_a_function with instance as this, so you can modify a_class_a_function like so:
function a_class_a_function() {
var self = this;
AFunctionThatTakesACallback(function() {
// do something with self.a_var
});
}
The problem here is that if you attempt to call a_class_a_function without calling it from an instance, then this will likely refer to the global object, window.
jQuery plugins use a pattern like this to hide private functions of a plugin:
(function ($) {
var a_private_function = function (opts) {
opts.onStart();
}
$.fn.name_of_plugin = function (options) {
a_private_function(opts);
}
})(jQuery);
jQuery then makes those fn functions available like this:
some_callback = function() {};
jQuery('selector').name_of_plugin( { onStart: some_callback } );
Now I'd like to override a_private_function. Is there any way I can access it without patching the actual plugin code?
I thought maybe I could access the execution context of the private function by using caller but that did not work:
some_callback = function() {
console.log(some_callback.caller.a_private_function); // -> undefined
};
jQuery('selector').name_of_plugin( { onStart: some_callback } );
As I learned in this answer, the only way to access the private members of a jQuery plugin are to modify the plugin source itself.
What you have there is a classical example of a closured function.
a_private_function is a function which is only visible within the scope from the "outer" anonymous function. Because of closure, the anonymous function assigned to name_of_plugin has access to the outer context and therefore a_private_function.
This is a good thing since you can protect and hide some of functions and variables.
Short story, there is absolutly zero chance to access a closured variable from the outside.
When using the JQUERY UI widget factory, the functions (which are prefixed with _) are not private, but instead (simulated) protected (prototype) functions.
This means you can access them as long as you extend the existing prototype. For example:
$.extend( $.ui.accordion.prototype, {
open: function( index ) {
//now you can access any protected function
var toOpen = self._findActive( index );
toOpen.next().show();
},
_completed: function ( cancel ) {
//You can even overwrite an existing function
}
});
The function you have demonstrated in your first example is, however, private - and therefore as the other answers suggest you cannot access these from the outside.
However, if you want to access protected variables inside a JQuery UI widget then this is possible (as above).
Thought this might be useful.