38

Is it possible to somehow pass the scope of a function to another?

For example,

function a(){
   var x = 5;
   var obj = {..};
   b(<my-scope>);
}

function b(){
   //access x or obj....
}

I would rather access the variables directly, i.e., not using anything like this.a or this.obj, but just use x or obj directly.

ErikE
  • 48,881
  • 23
  • 151
  • 196
ciochPep
  • 2,472
  • 4
  • 26
  • 30
  • By scope do you mean just methods or also values of all initialized in a() variables, etc ? btw (+1) for good question. – Scherbius.com Jun 14 '11 at 19:23
  • 1
    Also, you should accept answers to your questions if you've found them to be useful(See there is a tick there)and also use upvotes. It will help you get more answers. – Rishabh Jun 14 '11 at 19:25

11 Answers11

35

The only way to truly get access to function a's private scope is to declare b inside of a so it forms a closure that allows implicit access to a's variables.

Here are some options for you.

Direct Access

  1. Declare b inside of a.

    function a() {
       var x = 5,
          obj = {};
       function b(){
          // access x or obj...
       }
       b();
    }
    
    a();
    
  2. If you don't want b inside of a, then you could have them both inside a larger container scope:

    function container() {
       var x, obj;
       function a(){
          x = 5;
          obj = {..};
          b();
       }
       function b(){
          // access x or obj...
       }
    }
    
    container.a();
    

These are the only ways you're going to be able to use a's variables directly in b without some extra code to move things around. If you are content with a little bit of "help" and/or indirection, here are a few more ideas.

Indirect Access

  1. You can just pass the variables as parameters, but won't have write access except to properties of objects:

    function a() {
       var x = 5,
          obj = {};
       b(x, obj);
    }
    
    function b(x, obj){
       // access x or obj...
       // changing x here won't change x in a, but you can modify properties of obj
    }
    
    a();
    

    As a variation on this you could get write access by passing updated values back to a like so:

    // in a:
    var ret = b(x, obj);
    x = ret.x;
    obj = ret.obj;
    
    // in b:
    return {x : x, obj : obj};
    
  2. You could pass b an object with getters and setters that can access a's private variables:

    function a(){
       var x = 5,
          obj = {..},
          translator = {
             getX : function() {return x;},
             setX : function(value) {x = value;},
             getObj : function() {return obj;},
             setObj : function(value) {obj = value;}
          };
       b(translator);
    }
    
    function b(t){
       var x = t.getX(),
          obj = t.getObj();
    
       // use x or obj...
       t.setX(x);
       t.setObj(obj);
    
       // or you can just directly modify obj's properties:
       obj.key = value;
    }
    
    a();
    

    The getters and setters could be public, assigned to the this object of a, but this way they are only accessible if explicitly given out from within a.

  3. And you could put your variables in an object and pass the object around:

    function a(){
       var v = {
          x : 5,
          obj : {}
       };
       b(v);
    }
    
    function b(v){
       // access v.x or v.obj...
       // or set new local x and obj variables to these and use them.
    }
    
    a();
    

    As a variation you can construct the object at call time instead:

    function a(){
       var x = 5,
          obj = {};
       b({x : x, obj: obj});
    }
    
    function b(v){
       // access v.x or v.obj...
       // or set new local x and obj variables to these and use them.
    }
    
    a();
    
ErikE
  • 48,881
  • 23
  • 151
  • 196
9

Scope is created by functions, and a scope stays with a function, so the closest thing to what you're asking will be to pass a function out of a() to b(), and that function will continue to have access to the scoped variables from a().

function a(){
   var x = 5;
   var obj = {..};
   b(function() { /* this can access var x and var obj */ });
}
function b( fn ){

    fn(); // the function passed still has access to the variables from a()

}

While b() doesn't have direct access to the variables that the function passed does, data types where a reference is passed, like an Object, can be accessed if the function passed returns that object.

function a(){
   var x = 5;
   var obj = {..};
   b(function() { x++; return obj; });
}
function b( fn ){

    var obj = fn();
    obj.some_prop = 'some value'; // This new property will be updated in the
                                  //    same obj referenced in a()

}
ErikE
  • 48,881
  • 23
  • 151
  • 196
user113716
  • 318,772
  • 63
  • 451
  • 440
  • @patrick Where is the access to variable `a` in `b`? @gion sure, okay. :) – ErikE Jun 14 '11 at 20:10
  • @Erik: There is none, except in whatever capacity the function allows manipulation. *"While b() doesn't have direct access to the variables..."* – user113716 Jun 14 '11 at 20:13
  • @Erik: Well, the question's actual requirements are not supported by the language (direct access), but it does demonstrate that indirect access is possible, as shown by the `a++`. – user113716 Jun 14 '11 at 20:22
  • I was referring to "//access a or obj..." inside of `b`. Anyway, I don't mean to nitpick, I was trying to help you improve your answer. – ErikE Jun 14 '11 at 20:23
  • @Erik: Not a problem at all. Ultimately there's no way to *"access a or obj"* inside of `b()` because JavaScript is pass-by-value. The only thing `b()` will ever be able to get is a copy of the value that the `a()` function references via an intermediary function. That same function (or a different one) can overwrite the value with a new value passed from `b()`, or perform some other manipulation such as a `++`. It's basically all the same concept. ;o) – user113716 Jun 14 '11 at 20:32
  • @patrick_dw I'm sure you can inject a `with` or `eval` into the local scope in buggy browsers. – Raynos Jun 14 '11 at 20:34
  • @Erik: I think the main point of the answer, and the reason for the votes, is that scope is created by a function, and that function will never lose its scope, and as such will always be able to manipulate those variables in its scope. The specific manipulation needed will vary. – user113716 Jun 14 '11 at 20:38
8

what about using bind

function funcA(param) {     
    var bscoped = funcB.bind(this);     
    bscoped(param1,param2...)
}
vittore
  • 17,449
  • 6
  • 44
  • 82
  • if funcA is defined on global scope "this" passed through bind will be the window. No argument defined inside funcA will be readable inside funcB. Or am I missing something here? Still, when passing funcB.bind(this.funcA), I can only access the variables that are defined as funcA.variable. – Gabriel G Aug 27 '20 at 03:59
6

No.

You're accessing the local scope object. The [[Context]].

You cannot publicly access it.

Now since it's node.js you should be able to write a C++ plugin that gives you access to the [[Context]] object. I highly recommend against this as it brings proprietary extensions to the JavaScript language.

ErikE
  • 48,881
  • 23
  • 151
  • 196
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • 2
    Any reason for a downvote? If you can access `[[Context]]` please _do_ let me know. – Raynos Jun 14 '11 at 19:41
  • Just gave you an up vote. If any one writes this extension I would love to know and then it is not proprietary. It blows my mind that people refer to bind this as changing scope. All you are doing is changing a reference to an object and including it into the scope of the function. To actually change real "scope" you need to do as you said here and change the frame in which these things point at. – Tegra Detra Mar 08 '12 at 13:17
3

You can't "pass the scope"... not that I know of.
You can pass the object that the function is referring to by using apply or call and send the current object (this) as the first parameter instead of just calling the function:

function b(){
    alert(this.x);
}
function a(){
    this.x = 2;
    b.call(this);
}

The only way for a function to access a certain scope is to be declared in that scope.
Kind'a tricky.
That would lead to something like :

function a(){
    var x = 1;
    function b(){
        alert(x);
    }
}

But that would kind of defeat the purpose.

ErikE
  • 48,881
  • 23
  • 151
  • 196
gion_13
  • 41,171
  • 10
  • 96
  • 108
  • @Erik that's redundant. If I want to pass the vars as arguments, I wouldn't need scope. – gion_13 Jun 14 '11 at 19:42
  • @gion See my answer (third code block). I was suggesting something a little different than passing them as arguments. – ErikE Jun 14 '11 at 19:49
  • @Erik I know. You want to pass an object that would "simulate" the scope.I'm not saying it's wrong or that it won't work. It's great, but it's not quite the answer for this question. – gion_13 Jun 14 '11 at 19:54
  • @gion I find it helpful to give more choices instead of only answering exactly what was asked. For example, "you CAN do this with dynamic SQL but you probably shouldn't, here are other options". Anyway, I didn't downvote you if that's what you're thinking. – ErikE Jun 14 '11 at 19:57
  • @Erik: it has nothing to do with node.js. see http://www.java-samples.com/showtutorial.php?tutorialid=829 for more info – gion_13 Jun 15 '11 at 11:37
2

As others have said, you cannot pass scope like that. You can however scope variables properly using self executing anonymous functions (or immediately executing if you're pedantic):

(function(){
    var x = 5;
    var obj = {x:x};
    module.a = function(){
        module.b();
    };
    module.b = function(){
        alert(obj.x);    
    };
}());

a();
ErikE
  • 48,881
  • 23
  • 151
  • 196
david
  • 17,925
  • 4
  • 43
  • 57
  • it's annoying when people don't read the question and use browser js. It's just as bad as using jQuery in JavaScript only questions. `this` is `module` instead of `window` so just write to that. I also _highly_ recommend againts overwriting `global` as that's the _real_ global scope in node.js (and it may have unknown side-effects) – Raynos Jun 14 '11 at 19:47
  • @Raynos, I read the question, and knew it was in node. The window keyword was in there because I tested it in jsfiddle first, rather than blindly spewing code and hoping it works. My bad for not proofreading it again. – david Jun 14 '11 at 19:50
2

I think the simplest thing you can do is pass variables from one scope to a function outside that scope. If you pass by reference (like Objects), b has 'access' to it (see obj.someprop in the following):

function a(){
   var x = 5;
   var obj = {someprop : 1};
   b(x, obj);
   alert(x); => 5
   alert(obj.someprop); //=> 'otherval'
}
function b(aa,obj){
   x += 1; //won't affect x in function a, because x is passed by value
   obj.someprop = 'otherval'; //change obj in function a, is passed by reference
}
ErikE
  • 48,881
  • 23
  • 151
  • 196
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • As given, your `b` function will be accessing a global `a` and `obj`, not the ones in `a`. – ErikE Jun 14 '11 at 20:09
  • Yep, forgot the params in b(), edited, and by the way renamed var a, because that may conflict with the function name (http://jshint.com/ complained) – KooiInc Jun 14 '11 at 20:12
0

You can really only do this with eval. The following will give function b function a's scope

  function a(){
     var x = 5;
     var obj = {x};
     eval('('+b.toString()+'())');
  }
  
  function b(){
     //access x or obj....
     console.log(x);
  }
  
  a();  //5
Karol M
  • 21
  • 3
  • keep far away from eval.. Atleast use new Func – zergski Jun 30 '22 at 09:18
  • Yes use of eval is not ideal but it appears that this is the only solution that addresses and solves the question. However, the person who asked the question could potentially rework their code to eliminate the need for it. – Karol M Feb 11 '23 at 18:41
-2
function a(){
   this.x = 5;
   this.obj = {..};
   var self = this;
   b(self);
}
function b(scope){
   //access x or obj....

}
ErikE
  • 48,881
  • 23
  • 151
  • 196
thescientist
  • 2,906
  • 1
  • 20
  • 15
-2
function a(){
   var x = 5;
   var obj = {..};
   var b = function()
   {
        document.println(x);
   }
   b.call();
}
ErikE
  • 48,881
  • 23
  • 151
  • 196
Shawn
  • 19,465
  • 20
  • 98
  • 152
  • It's not the best answer, but it's a good one. One of the ways to pass the scope is by defining the function in that scope, so stop downvoting this – gion_13 Jun 14 '11 at 19:58
  • 1
    Shawn there's no need for `b.call();`. Just `b();` directly. – ErikE Jun 14 '11 at 22:18
-2

Have you tried something like this:

function a(){
   var x = 5;
   var obj = {..};
   b(this);
}
function b(fnA){
   //access x or obj....
   fnA.obj = 6;
}

If you can stand function B as a method function A then do this:

function a(){
   var x = 5;
   var obj = {..};
   b(this);

   this.b = function (){
      // "this" keyword is still === function a
   }
}
ErikE
  • 48,881
  • 23
  • 151
  • 196
kgb
  • 1
  • 1. There's no need for using `this` at all. Just declaring `b` inside of `a` will automatically grant `b` access to `a`'s private variables. 2. Using the `this` keyword in `b` will not give access to the private variables so `this.obj` will be undefined in `b`. – ErikE Jun 14 '11 at 20:02