54

I was wondering if there is any way to access variables trapped by closure in a function from outside the function; e.g. if I have:


A = function(b) {
    var c = function() {//some code using b};
    foo: function() {
        //do things with c;
    }
}

is there any way to get access to c in an instance of A. Something like:


var a_inst = new A(123);
var my_c = somejavascriptmagic(a_inst);
Kiersten Arnold
  • 1,840
  • 1
  • 13
  • 17

8 Answers8

26

A simple eval inside the closure scope can still access all the variables:

function Auth(username)
{
  var password = "trustno1";
  this.getUsername = function() { return username }
  this.eval = function(name) { return eval(name) }
}

auth = new Auth("Mulder")
auth.eval("username") // will print "Mulder"
auth.eval("password") // will print "trustno1"

But you cannot directly overwrite a method, which is accessing closure scope (like getUsername()), you need a simple eval-trick also:

auth.eval("this.getUsername = " + function() {
  return "Hacked " + username;
}.toSource());
auth.getUsername(); // will print "Hacked Mulder"
kungfooman
  • 4,473
  • 1
  • 44
  • 33
  • 1
    Wow, this trick really works. I wouldn't have thought that defining a new property on an extant object would allow `eval` access to any of its closures. This technique helped me monkey-patch a script processed through that webpack that couldn't be runtime-altered any other way. – Dai Mar 23 '20 at 02:52
  • @Dai Glad that it helped, JavaScript is really powerful and dynamic when going into the filthy details – kungfooman Mar 25 '20 at 11:12
  • 2
    Tested this in the console and learned support for the method `toSource()` has become scarce. You can achieve the same thing using `toString()` in its place. – Jetals Aug 28 '20 at 15:38
  • Mozilla docs say "never use eval" - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval – OutstandingBill Feb 27 '23 at 00:06
  • @OutstandingBill They also fire all developers and increase the manager salaries... – kungfooman Feb 27 '23 at 10:43
22

Variables within a closure aren't directly accessible from the outside by any means. However, closures within that closure that have the variable in scope can access them, and if you make those closures accessible from the outside, it's almost as good.

Here's an example:

var A = function(b) {
    var c = b + 100;
    this.access_c = function(value) {
        // Function sets c if value is provided, but only returns c if no value
        // is provided
        if(arguments.length > 0)
            c = value;
        return c;
    };
    this.twain = function() {
        return 2 * c;
    };
};
var a_inst = new A(123);
var my_c = a_inst.access_c();
// my_c now contains 223
var my_2c = a_inst.twain();
// my_2c contains 446
a_inst.access_c(5);
// c in closure is now equal to 5
var newer_2c = a_inst.twain();
// newer_2c contains 10

Hopefully that's slightly useful to you...

psmay
  • 1,001
  • 7
  • 17
  • 8
    Unfortunately, I can't modify A (it's in a library) so it looks like the consensus is that c is inaccessible to outside functions. – Kiersten Arnold Dec 17 '10 at 17:03
10

Answers above are correct, but they also imply that you'll have to modify the function to see those closed variables.

Redefining the function with the getter methods will do the task. You can do it dynamically. See the example below

function alertMe() {
    var message = "Hello world"; 
    console.log(message);
}

//adding the getter for 'message'
var newFun = newFun.substring(0, newFun.lastIndexOf("}")) + ";" + "this.getMessage = function () {return message;};" + "}";

//redefining alertMe
eval(newFun);

var b = new alertMe();

now you can access message by calling b.getMesage()

Of course you'll have to deal with multiple calls to alertMe, but its just a simple piece of code proving that you can do it.

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
traveller
  • 101
  • 1
  • 3
4

The whole point to that pattern is to prevent 'c' from being accessed externally. But you can access foo() as a method, so make it that it will see 'c' in its scope:

A = function(b) {
    var c = function() {//some code using b};
    this.foo = function() {
        return c();
    }
}
mwilcox
  • 4,065
  • 23
  • 20
2

No, not without a getter function on A which returns c

Gareth
  • 133,157
  • 36
  • 148
  • 157
  • Could you define a function on A dynamically to do this like:
    A.prototype.getC = function() {return c;}
    or would it have to be defined in the definition of A?
    – Kiersten Arnold Dec 17 '10 at 16:27
  • Well, just answered my own question and the answer is a definitive no. I'm guess c is only available in the scope of A = function{...}. – Kiersten Arnold Dec 17 '10 at 17:00
2

If you only need access to certain variables and you can change the core code there's one easy answer that won't slowdown your code or reasons you made it a closure in any significant way. You just make a reference in the global scope to it basically.

(function($){
    let myClosedOffObj = {
        "you can't get me":"haha getting me would be useful but you can't cuz someone designed this wrong"
    };
    window.myClosedOffObj = myClosedOffObj;
})(jQuery);
myClosedOffObj["you can't get me"] = "Got you now sucker";

Proof of concept: https://jsfiddle.net/05dxjugo/

This will work with functions or "methods" too.

0

If none of the above is possible in your script, a very hacky solution is to store it in a hidden html-object:

// store inside of closure
html.innerHTML+='<div id="hiddenStore" style="display:none"></div>';
o=document.getElementById("hiddenStore")
o.innerHTML="store this in closure"

and outside you can read it with

document.getElementById("hiddenStore").innerHTML
rubo77
  • 19,527
  • 31
  • 134
  • 226
-4

You should be able to use an if statement and do something like:

if(VaraiableBeingPasses === "somethingUniqe") {
    return theValueOfC;
}
Kelvin Schoofs
  • 8,323
  • 1
  • 12
  • 31