Javascript: Member-variable-accessing closures - javascript

I'm wondering how to deal with member variables in closures in JavaScript. The following code alerts "6".
function testy()
{
function blah()
{
this.a = 5;
this.func = function()
{
this.a = 6;
alert(this.a);
}
}
var x = new blah;
x.func();
}
but this code alerts 5.
function testy()
{
function execute(func)
{
func();
}
function blah()
{
this.a = 5;
this.func = function()
{
execute(function()
{
this.a = 6;
});
alert(this.a);
}
}
var x = new blah;
x.func();
}
How do I pass a closure which still accesses the member variables of the enclosing object?

execute(function()
{
this.a = 6;
});
function execute(func)
{
func();
}
Your calling the function as func(); and by default without specifying a context this will resolve to the global context which is window in the browser.. There are three options you can use here.
make this local
var that = this;
execute(function()
{
that.a = 6;
});
Now that points to the correct this.
bind this scope to the function
execute((function()
{
this.a = 6;
}).bind(this));
This will bind the correct / expected this scope to your function. Note that Function.prototype.bind is ES5 and will break older browsers. _.bind is a reasonable cross browser alternative.
edit execute
function execute(f, context) {
f.call(context);
}
execute(function() {
this.a = 6;
}, this);
Your passing the context as an extra parameter to execute. Then execute will call Function.prototype.call to make sure that the function is called with the desired context

Try this:
function blah()
{
this.a = 5;
this.func = function()
{
var self = this;
execute(function()
{
self.a = 6;
});
alert(this.a);
}
}

Related

Pass a function as a parameter in JS and access the receiving function's variables

I'm not sure this is possible in Javascript. I want to access a function's variable through a passed function, (or if a function is passed via an 'onend' call).
function Outer() {
var fn;
var foo = 'this is foo';
this.bar = function(x) {
fn();
}
this.setFunction = function(f) {
fn = f;
}
}
var o = new Outer();
o.setFunction(function() {
alert(foo); //doesn't work
});
o.bar(); //want to alert 'this is foo'
fn in your case is functionally a callback. Call the callback with the local foo variable as an argument:
function Outer() {
var fn;
var foo = 'this is foo';
this.bar = function() {
fn(foo);
}
this.setFunction = function(f) {
fn = f;
}
}
var o = new Outer();
o.setFunction(function(foo) {
alert(foo);
});
o.bar();
Could you try this
function Outer() {
var fn;
this.foo = 'this is foo';
this.bar = function(x) {
fn();
}
this.setFunction = function(f) {
fn = f;
}
}
var o = new Outer();
o.setFunction(function() {
alert(o.foo);
});
o.bar(); //want to alert 'this is foo'
and tell us if is what you need.
thanks

Accessing outer scope from inner scope

I have a type that looks a little something like this:
var x = function(){
this.y = function(){
}
this.z = function(){
...
this.A = function(){
CALLING POINT
}
}
}
From calling point, I'm attempting to call function this.y. I don't need to pass any parameters, but when I set some stuff from this.A, I need to call this.y.
Is this possible? I'm OK with passing extra parameters to functions to make it possible.
Is this possible?
Yes, you can assign this reference to another variable and then call function y on it
this.z = function() {
var self = this;
this.A = function() {
self.y();
}
}
Version with bind, basically this adds a new method a to the object.
var X = function () {
this.y = function () {
document.write('y<br>');
}
this.z = function () {
document.write('z<br>');
this.a = function () {
document.write('a<br>');
this.y();
}
}.bind(this);
};
var x = new X;
//x.a(); // does not exist
x.z(); // z
x.a(); // a y
Working example with saved inner this.
var X = function () {
var that = this; // <--
this.y = function () {
document.write('y<br>');
}
this.Z = function () {
document.write('Z<br>');
this.a = function () {
document.write('a<br>');
that.y();
}
}
}
var x = new X,
z = new x.Z; // Z
z.a(); // a y
Instead of function() you can try modern JavaScript or Typescript ()=>. I also like .bind(this).
You cannot because this.y() is not within the scope that this.A() is in. You can if you set this.y() to a global function y:
var y = function() {};
var x = function() {
this.y = y;
this.z = function() {
...
this.A = function() {
this.y(); // will be successful in executing because this.y is set to the y function.
};
}
};

Try to create object of returned function?

I was trying something different and ended up with these codes..
var f1 = function() {
this.x = 10;
this.innerf = function() { console.log(this.x); }
}
var of1 = new f1();
of1.innerf();
var f2 = function() {
return function() {
this.x = 10;
this.innerf = function() { console.log(this.x); }
}
}
var of2 = new f2();
of2.innerf();
It is throwing error ??! of2.inner is not a function
So, my anonymous function is returning same function body to my variable.
Why still i cannot able to instantiate??
The first part returns an object of which you can call the innerf method.
The second part returns a function that would return an object if you called it. But you don't.
This would work. Call the function f2(). It's return value is the anonymous function. Then, with new <return value of f2>(), you can create an instance of the object.
var f2 = function() {
return function() {
this.x = 10;
this.innerf = function() { console.log(this.x); }
}
}
var of2 = new (f2())();
of2.innerf();
// The two lines above can also be written as:
var of3constructor = f2(); // This returns the inner anonymous function.
var of3 = new of3constructor(); // This creates an instance by invoking the anonymous function.
of3.innerf();
Examples that work:
Creating it directly:
var f1 = function() {
this.x = 11;
this.innerf = function() {
console.log(this.x);
}
}
var of1 = new f1();
of1.innerf();
Returning a new object from a function:
var f2 = function() {
return new function() {
this.x = 12;
this.innerf = function() {
console.log(this.x);
}
}
}
var of2 = f2();
of2.innerf();
Returning an object:
var f3 = function() {
return {
x: 13,
innerf : function() {
console.log(this.x);
}
}
}
var of3 = f3();
of3.innerf();
Another one:
var f4 = function() {
return function() {
this.x = 10;
this.innerf = function() {
console.log(this.x);
}
}
}
var of4 = new (f2())();
of2.innerf();
Remember that when you call a function without the "new" keyword "this" points object where the functions was declared, in this case "window"
You need to return this from the inner function to the outer function. Also you need to run the inner function immediately.
jsFiddle:
http://jsfiddle.net/5dxybbb5/
var f1 = function() {
this.x = 10;
this.innerf = function() {console.log(this.x);}
}
var of1 = new f1();
of1.innerf();
var f2 = function() {
return function() {
this.x = 10;
this.innerf = function() {console.log(this.x);}
return this;
}();
}
var of2 = new f2();
of2.innerf();
Also this explains the () after a function declaration
What is the (function() { } )() construct in JavaScript?

'this' is unequal to Bar in prototype

In the following code snippet, 'this.x()' can only be called in case 2 (see main()).
Also Bar unequals this in case 1, but is equal for case 2.
function Class_Bar() {
this.panel = null;
this.init = function () {
// do some stuff
this.panel = 20;
}
this.apply = function () {
alert(Bar == this);
Bar.x();
this.x();
}
this.x = function() {
alert("Some friendly message");
alert(Bar.panel);
}
}
var Bar = new Class_Bar();
function Class_Factory() {
this.factories = new Array();
this.add = function (init, apply) {
this.factories.push({"init":init, "apply":apply});
}
this.init = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["init"]();
}
}
this.apply = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["apply"]();
}
}
}
var Factory = new Class_Factory();
function main() {
// Case 1
Factory.add(Bar.init, Bar.apply);
Factory.init();
Factory.apply();
// Case 2
Bar.init();
Bar.apply();
}
main();
http://pastebin.com/fpjPNphx
Any ideas how to "fix" / workaround this behaviour?
I found a possible solution, but it seems to be a "bad" hack.: Javascript: How to access object member from event callback function
By passing Bar.init, you're really only passing the function but not the information that it belongs to Bar (i.e. what the this value should be). What you can do is binding that information:
Factory.add(Bar.init.bind(Bar), Bar.apply.bind(Bar));

jquery event function call with oop?

var objs = new Array();
function Foo(a) {
this.a = a
$("#test").append($("<button></button>").html("click").click(this.bar));
}
Foo.prototype.bar = function () {
alert(this.a);
}
$(document).ready(function () {
for (var i = 0; i < 5; i++) {
objs.push(new Foo(i));
}
});
is it possible to make it so that when a button is clicked,
it returns corresponding Foo.a value (from Foo obj that created the button)?
The #Khnle's answer is close, but with that approach you need an anonymous function to use the self reference:
function Foo(a) {
this.a = a;
var self = this;
$("#test").append($("<button></button>").html("click").click(function () {
self.bar(); // correct `this` value inside `bar`
}));
}
You can also use the $.proxy method, to preserve the object context:
function Foo(a) {
this.a = a
$("#test").append($("<button></button>")
.html("click")
.click($.proxy(this.bar, this)));
}
Check the above example here.
Inside your click handler, this no longer refers to the Foo object, but the element where click takes place, which is the button element in this case. So one way to fix it is as follow:
function Foo(a) {
this.a = a;
var self = this;
$("#test").append($("<button></button>").html("click").click(self.bar));
}
Here's more jQuery style (no global objs or Foos)
(function ($) {
$.fn.aghragbumgh = function (settings) {
var config = { 'a': 0 };
if (settings) $.extend(config, settings);
var bar = function () {
alert(config.a);
}
this.each(function () {
$(this).append($("<button></button>").html("click").click(bar));
});
return this;
};
})(jQuery);
$(document).ready(function () {
for (var i = 0; i < 5; i++) {
$('#test').aghragbumgh({ 'a': i });
};
});
Html:
<div id="test"></div>

Categories