Samson
Samson

Reputation: 2821

Javascript value in closure

I really can't explain myself why I failed in creating a closure and I get undefined in the code below:

function A() {
 this.p1=1;
 this.p2=2;
 this.f1=function(){
    alert(this.p1);
 }
 this.f2=function(){
    alert(this.p2);
    }
 this.action=function(param){
    if (param=='1')
        return this.f1;//by case
    else if (param=='2')
        return this.f2;
    };
}

var v=new A();
v.action("1")();

Upvotes: 1

Views: 97

Answers (6)

zpea
zpea

Reputation: 1082

As you intended

v.action("1")

returns the function defined as

function(){
    alert(this.p1);
}

You can also call it by adding a pair of brackets, like you did. However the this in that function will be bound to the global object (in browser it is window). And window.p1 is undefined. That's what you get as output.

As mentioned already, you can decide what should be this in the function by calling it using call instead of the brackets:

v.action("1").call(v);

What this means in JavaScript is quite confusing. An explanation that helped me understand can be found in section 3.3 The meaning of this (the egregious disaster) of Javascript in Ten Minutes (a worthwhile read though the whole document takes a lot more than ten minutes).

Upvotes: 0

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76395

Looks to me like you're not 100% sure on what a closure is... Starting with your code:

function A()
{
    this.p1=1;//public
    var p2=2;//private
    this.f1=function()
    {
        alert(this.p1);
    };
    this.f2=function()
    {
        alert(p2);
    };

Here, I've defined p2 as a variable, local to the constructor's scope, yet the public method f2 can sitl access its value, change it even. That's a closure: code/data contained within its own specific scope. What you're doing with the following code:

    this.action=function(param)
    {
        if (param=='1')
        {
            return this.f1;
        }
        return this.f2;
    };
}
var v=new A();
v.action("1")();

Has little or nothing to do with a closure, you're returning a reference to a function (that happens to be an object method in this specific case). Why, then doesn't this point to the object it came from? Simple:

var foo = {name:'someObject'};
foo.retVal = v.action('1');//returns function
//much later:
foo.retVal();

Would you expect this to point to v, still? And wouldn't that be hellish to debug? upon calling a function (method or not), this will be the pointer of the context-object in which the function is called. Once v.action('1') returns, in your example, the function is returned to the global object, hence this will point to this or window

Upvotes: 1

glebm
glebm

Reputation: 21090

The functions are not binded to a specific this, that is to say:

(function() { console.log(this) })(); // Window

You can either use the call method, or (better) use the .bind method (make sure to polyfill for older browsers):

function A() {
 this.p1 = 1;
 this.p2 = 2;
}
A.prototype.f1 = function() {alert(this.p1)};
A.prototype.f2 = function() {alert(this.p2)};
A.prototype.action = function(param) {
  if ('1' == param) {
    return this.f1.bind(this);
  } else if (param == '2') {
    return this.f2.bind(this);
  }
};

var v = new A();
v.action("1")(); // 1

The bind method returns a new function which calls the original function with this set to the bind's argument. Function.prototype.bind documentation on MDN

v.action("1") is roughly the same as action.call(v, "1")

Upvotes: 1

Goran Obradovic
Goran Obradovic

Reputation: 9051

Inside your nested function this has another meaning. Try using variable to pass reference inside, see jsfiddle.

Upvotes: 0

johnrom
johnrom

Reputation: 672

Inside of a closure like this, "this" actually refers to the Window when it is called later. In order to avoid this, you need to assign "this" to another variable, like "that":

function A() {
var that = this;
 that.p1=1;
 that.p2=2;
 that.f1=function(){
    alert(that.p1);
 }
 that.f2=function(){
    alert(that.p2);
    }
 that.action=function(param){
    if (param=='1')
        return that.f1;//by case
    else if (param=='2')
        return that.f2;
    };
}

var v=new A();
v.action("1")();

Upvotes: 0

Guffa
Guffa

Reputation: 700342

That has nothing to do with a closure.

You are returning a function reference from the method, and when you call it you expect it to be executed as a method in the object, but it's executed as a function with the global scope as context.

Use the call method to call the function in the context of the object:

v.action("1").call(v);

Upvotes: 4

Related Questions