JavaScript pass scope to another function
Asked Answered
C

11

40

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.

Comedienne answered 14/6, 2011 at 19:20 Comment(2)
By scope do you mean just methods or also values of all initialized in a() variables, etc ? btw (+1) for good question.Enzootic
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.Sices
A
37

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();
    
Aegir answered 14/6, 2011 at 19:48 Comment(0)
B
10

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()

}
Boloney answered 14/6, 2011 at 19:47 Comment(7)
@patrick Where is the access to variable a in b? @gion sure, okay. :)Aegir
@Erik: There is none, except in whatever capacity the function allows manipulation. "While b() doesn't have direct access to the variables..."Boloney
@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++.Boloney
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.Aegir
@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)Boloney
@patrick_dw I'm sure you can inject a with or eval into the local scope in buggy browsers.Triarchy
@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.Boloney
P
8

what about using bind

function funcA(param) {     
    var bscoped = funcB.bind(this);     
    bscoped(param1,param2...)
}
Porky answered 14/6, 2011 at 21:37 Comment(1)
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.Kilmer
T
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.

Triarchy answered 14/6, 2011 at 19:33 Comment(2)
Any reason for a downvote? If you can access [[Context]] please do let me know.Triarchy
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.Violaviolable
M
4

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.

Margrettmarguerie answered 14/6, 2011 at 19:31 Comment(5)
@Erik that's redundant. If I want to pass the vars as arguments, I wouldn't need scope.Margrettmarguerie
@gion See my answer (third code block). I was suggesting something a little different than passing them as arguments.Aegir
@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.Margrettmarguerie
@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.Aegir
@Erik: it has nothing to do with node.js. see java-samples.com/showtutorial.php?tutorialid=829 for more infoMargrettmarguerie
S
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();
Sausauce answered 14/6, 2011 at 19:40 Comment(2)
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)Triarchy
@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.Sausauce
Y
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
}
Yacano answered 14/6, 2011 at 19:53 Comment(2)
As given, your b function will be accessing a global a and obj, not the ones in a.Aegir
Yep, forgot the params in b(), edited, and by the way renamed var a, because that may conflict with the function name (jshint.com complained)Yacano
K
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
Knitter answered 22/4, 2021 at 19:42 Comment(2)
keep far away from eval.. Atleast use new FuncHymenopterous
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.Knitter
B
-2
function a(){
   this.x = 5;
   this.obj = {..};
   var self = this;
   b(self);
}
function b(scope){
   //access x or obj....

}
Brazilin answered 14/6, 2011 at 19:25 Comment(1)
This is context, rather than scope.Sausauce
D
-2
function a(){
   var x = 5;
   var obj = {..};
   var b = function()
   {
        document.println(x);
   }
   b.call();
}
Drusilla answered 14/6, 2011 at 19:26 Comment(2)
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 thisMargrettmarguerie
Shawn there's no need for b.call();. Just b(); directly.Aegir
A
-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
   }
}
Amid answered 14/6, 2011 at 19:44 Comment(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.Aegir

© 2022 - 2024 — McMap. All rights reserved.