Variable scope and closure

  • Need help?
    Reach out to someone from Samaritans of Singapore (24 hours): Call 1-767 or WhatsApp 9151-1767 More info

u0206397

Senior Member
Joined
Jul 15, 2009
Messages
764
Reaction score
0
Hey JavaScript experts!

I have a piece of code that looks like this:

PHP:
function outer(){
    var outerVariable = "hello world";

    var inner = function(){
        alert(outerVariable); // Notice how it can access outerVariable due to closure
    }

    call_func( inner ); // inner is given as a callback function
}

Now I realize the inner is used in multiple places. So I shifted the inner out to same level of outer. Now inner cannot access outerVariable.

PHP:
function outer(){
    var outerVariable = "hello world";

    call_func( inner ); // inner is given as a callback function
}

function outer2(){  // Same as outer, trying to reuse inner function.
    var outerVariable = "goodbye world";

    call_func( inner );
}

function inner(){
    alert(outerVariable); // Fails! Cannot find outerVariable!
}

How should we handle this? Duplicating the inner in both outer and outer2 would obviously work, but not great for code reuse. :(
 

KnightNiwrem

Senior Member
Joined
Jun 1, 2014
Messages
1,057
Reaction score
0
Hey JavaScript experts!

I have a piece of code that looks like this:

PHP:
function outer(){
    var outerVariable = "hello world";

    var inner = function(){
        alert(outerVariable); // Notice how it can access outerVariable due to closure
    }

    call_func( inner ); // inner is given as a callback function
}

Now I realize the inner is used in multiple places. So I shifted the inner out to same level of outer. Now inner cannot access outerVariable.

PHP:
function outer(){
    var outerVariable = "hello world";

    call_func( inner ); // inner is given as a callback function
}

function outer2(){  // Same as outer, trying to reuse inner function.
    var outerVariable = "goodbye world";

    call_func( inner );
}

function inner(){
    alert(outerVariable); // Fails! Cannot find outerVariable!
}

How should we handle this? Duplicating the inner in both outer and outer2 would obviously work, but not great for code reuse. :(

From a software engineering standpoint, it doesn't seem like outer, outer2, or inner does anything more than to alert. Might as well just use alert, itself.

From an academic standpoint, though. It is unclear what call_func does, other than to call the function. There doesn't seem to be a reason why you can't directly use inner(outerVariable) in the outer and outer2 function body, and have inner take in a single argument called outerVariable.
 

davidktw

Arch-Supremacy Member
Joined
Apr 15, 2010
Messages
13,547
Reaction score
1,301
Hey JavaScript experts!

I have a piece of code that looks like this:

PHP:
function outer(){
    var outerVariable = "hello world";

    var inner = function(){
        alert(outerVariable); // Notice how it can access outerVariable due to closure
    }

    call_func( inner ); // inner is given as a callback function
}

Now I realize the inner is used in multiple places. So I shifted the inner out to same level of outer. Now inner cannot access outerVariable.

PHP:
function outer(){
    var outerVariable = "hello world";

    call_func( inner ); // inner is given as a callback function
}

function outer2(){  // Same as outer, trying to reuse inner function.
    var outerVariable = "goodbye world";

    call_func( inner );
}

function inner(){
    alert(outerVariable); // Fails! Cannot find outerVariable!
}

How should we handle this? Duplicating the inner in both outer and outer2 would obviously work, but not great for code reuse. :(

closures are not technique for code reuse-ability, it is a scoping feature for data binding. Don’t mixed up between them. You use closures when you want to exercise early binding instead of late binding. If you want to invoke different values inside inner function, pass in a variable. Javascript does not have dynamic scoping, that is how you will do it if you want to reference the same variable inside inner function but want the value to be late binded
 

u0206397

Senior Member
Joined
Jul 15, 2009
Messages
764
Reaction score
0
From a software engineering standpoint, it doesn't seem like outer, outer2, or inner does anything more than to alert. Might as well just use alert, itself.

This is an code snippet example to simply illustrate the current code structure. My purpose is to seek opinions how to refactor this code -- rewrite inner as a named function that can be used as a common callback function by multiple functions.

From an academic standpoint, though. It is unclear what call_func does, other than to call the function. There doesn't seem to be a reason why you can't directly use inner(outerVariable) in the outer and outer2 function body, and have inner take in a single argument called outerVariable.

call_func is a function that does async operation that takes in a user defined callback function. call_func is from a third party library which I have no control over, which is why I cannot redefine the callback function and the parameters it can receive. It so happens that the callback function needs to access certain variables in my code which are not being passed in to it by call_func.

Currently, I am using (or abusing ;) ) closure which exposes the variables to the callback function, even though they are not declared within it, or passed as parameters to it, as seen in the first code snippet.
 

u0206397

Senior Member
Joined
Jul 15, 2009
Messages
764
Reaction score
0
closures are not technique for code reuse-ability, it is a scoping feature for data binding. Don’t mixed up between them. You use closures when you want to exercise early binding instead of late binding. If you want to invoke different values inside inner function, pass in a variable. Javascript does not have dynamic scoping, that is how you will do it if you want to reference the same variable inside inner function but want the value to be late binded

I get what you mean. I didn't intentionally choose to write my code in this way, except there is an async operation done by a third party library, and it takes in a callback function which is called after it is done.

So some people on StackOverflow recommended anonymous function and closure as a way of getting around this limitation.

Darn! I just found another explanation on StackOverflow regarding this. It's not just me wrestling with callback and closures! :s22:
 

u0206397

Senior Member
Joined
Jul 15, 2009
Messages
764
Reaction score
0
pass outerVariable in as an argument for inner?

Forgot to say call_func is something from a third party code. I cannot change it to pass in more parameters, unless I hack the library. That's something I won't want to do if possible. Some JavaScript functions that take in callbacks do allow for additional parameters and variables to be passed in to the callback function though, like setTimeout for example.

PHP:
setTimeout(function, milliseconds, param1, param2, ...)

  1. function: Required. The function that will be executed
  2. milliseconds: Optional. The number of milliseconds to wait before executing the code. If omitted, the value 0 is used
  3. param1, param2, ...: Optional. Additional parameters to pass to the function

But not everyone catered for additional parameters for a callback function. :(
 

u0206397

Senior Member
Joined
Jul 15, 2009
Messages
764
Reaction score
0
pass outerVariable in as an argument for inner?

OK. I decided to take your idea and merge with the idea from StackOverflow known as currying: a function that returns a function.

PHP:
function outer(){ 
    var outerVariable = "hello world"; 

    call_func( inner(outerVariable) ); // outerVariable passed in
} 

function outer2(){  // Same as outer, trying to reuse inner function. 
    var outerVariable = "goodbye world"; 

    call_func( inner(outerVariable) ); // outerVariable passed in
} 

function inner( message ){ // message is actually outerVariable
    return function () {  // This now returns a function
        alert( message ); // This can get the message parameter in scope
    } 
}

The fundamental rule is inner can never access variables within outer and outer2 directly due to scoping.
 

davidktw

Arch-Supremacy Member
Joined
Apr 15, 2010
Messages
13,547
Reaction score
1,301
OK. I decided to take your idea and merge with the idea from StackOverflow known as currying: a function that returns a function.

PHP:
function outer(){ 
    var outerVariable = "hello world"; 

    call_func( inner(outerVariable) ); // outerVariable passed in
} 

function outer2(){  // Same as outer, trying to reuse inner function. 
    var outerVariable = "goodbye world"; 

    call_func( inner(outerVariable) ); // outerVariable passed in
} 

function inner( message ){ // message is actually outerVariable
    return function () {  // This now returns a function
        alert( message ); // This can get the message parameter in scope
    } 
}

The fundamental rule is inner can never access variables within outer and outer2 directly due to scoping.

Good you found your dynamic closure, in fact still OOP since functions are first class citizen objects in javascript and the parameters to the function are private variables :)
 

davidktw

Arch-Supremacy Member
Joined
Apr 15, 2010
Messages
13,547
Reaction score
1,301
To further discuss on scoping. Here is a dynamic scoping in Perl

Code:
$ cat test.pl
#!/usr/bin/env perl

use strict;
use warnings;

our $x = 1;
our $l = '';

sub A {
	local $x = 2;
	local $l = "Inside A now: ";
	C();
}

sub B {
	local $x = 3;
	local $l = "Inside B now: ";
	C();
}

sub C {
	$l = "Inside C now: " unless ($l);
	print "${l}x = $x\n";
}

sub D {
	local $x = 4;
	local $l = "Inside D->E->F->C now: ";
	E();
}

sub E {
	F();
}

sub F {
	C();
}

A();
B();
C();
D();

Inside A now: x = 2
Inside B now: x = 3
Inside C now: x = 1
Inside D->E->F->C now: x = 4

Do some simple tracing and you will understand why the values are printed as such. This is not closure, but rather the manipulation of dynamically chained symbol tables in language design to produce such scoping effects.
 
Last edited:

cwchong

Master Member
Joined
Jan 7, 2005
Messages
4,654
Reaction score
96
if ‘something’ is to be used somewhere else, i think it fits the service pattern pretty well

Are u familiar with the different patterns like factory, singleton etc?
 

u0206397

Senior Member
Joined
Jul 15, 2009
Messages
764
Reaction score
0
if ‘something’ is to be used somewhere else, i think it fits the service pattern pretty well

Are u familiar with the different patterns like factory, singleton etc?

Would be great if you can demonstrate the application of service pattern using code snippet. Good code is short, elegant and self-explanatory. How would you rewrite my example if we apply the service pattern?
 

cwchong

Master Member
Joined
Jan 7, 2005
Messages
4,654
Reaction score
96
Would be great if you can demonstrate the application of service pattern using code snippet. Good code is short, elegant and self-explanatory. How would you rewrite my example if we apply the service pattern?

maybe something like:
Code:
var MyService = (function () {
    var instance;
 
    function createInstance() {
        var object = { value: null };
        return object;
    }
 
    return {
        setValue: function(v) {
            if (!instance) {
                instance = createInstance();
            }
            instance.value = v;
        },
        doJob: function() {
            if (!instance) {
                instance = createInstance();
            }
            console.log('value is', instance.value);
        }
    };
})();

(function() { // run 1
    var myValue = 10;
    MyService.setValue(myValue);
    MyService.doJob();
})();

(function() { // run 2
    var myValue = 99;
    MyService.setValue(myValue);
    MyService.doJob();
})();

note there is a potential async issue since our "doJob()" is just doing that; to avoid that, you should return a Promise object instead
 
Important Forum Advisory Note
This forum is moderated by volunteer moderators who will react only to members' feedback on posts. Moderators are not employees or representatives of HWZ Forums. Forum members and moderators are responsible for their own posts. Please refer to our Community Guidelines and Standards and Terms and Conditions for more information.
Top