3
function a(){ return x++; }
function b(){ var x=0; return a(); /*call a using b's scope*/ };
console.log(b(),b(),b());

Desired output: 1, 2, 3

Question: is there a way to call a inside b, forcing it to use b's scope?

James Black
  • 41,583
  • 10
  • 86
  • 166
MaiaVictor
  • 51,090
  • 44
  • 144
  • 286

6 Answers6

3

Not quite, but you can wrap a closure around it:

(function() {
    var x = 0;
    window.a = function() {return x++;};
    window.b = function() {return a();};
    console.log(b(),b(),b());
})();
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
1

No. Scope comes solely from how the function is defined.

You would have to pass data using arguments (and a closure) to get something from the scope of b into a.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
1

Your exact example isn't going to work (because x is reinitialized every time you call b), but consider:

x = 100;
function a() { return x++; }
function b() { 
    var x = 0; 
    console.log(a(), a(), a()); // 100 101 102
    eval(a.toString())
    console.log(a(), a(), a()); // 0 1 2
}

Needless to say, don't try that at work.

georg
  • 211,518
  • 52
  • 313
  • 390
1

Your problem can be decomposed into two parts:

  1. Dynamic scoping - http://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping
  2. Variable preservation across calls

The first part can be easily tackled using eval. Read the following StackOverflow question for more details: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?

The second part requires that you store the intermediate value of the variable x somewhere and restore it after every function call. The best place to store it in this case would be on the function b itself.

This is what your code would look like:

function a() {
    return ++x; // I believe you want prefix increment, not postfix
}

function b() {
    var x = b.x;
    eval(String(a)); // dynamic scoping
    b.x = a();
    return x;
}

b.x = 0;

console.log(b(), b(), b());

You can see the demo here: http://jsfiddle.net/ucGxD/1/

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • @JanTuroň I agree `eval` is evil in _most cases_. However that doesn't mean that `eval` itself is inherently evil. It just means that it's misused by novice programmers causing a lot of problems. In the above example it's perfectly alright to use `eval` because AFAIK there is absolutely no other way to achieve dynamic scoping in JavaScript. Of course the above program can be rewritten without using `eval` as Kolink answered but I just answered the question at the face value to show a different perspective. There are some real use cases of `eval`. Someone thought it was needed and so JS has it. – Aadit M Shah Mar 24 '13 at 14:49
  • 1
    Eval might be useful in situations like running JS code from textbox. However, from what I understand dynamic scoping is exactly what call and apply do, see my answer. – Jan Turoň Mar 24 '13 at 14:56
  • @JanTuroň - I agree, your solution is cleaner and that is what I would do too. However I wrote my answer given the constraints provided by the OP. If `x` absolutely must be a local variable then AFAIK there's no other solution other than to use `eval`. Neither `call` nor `apply` would work in that case. – Aadit M Shah Mar 24 '13 at 15:05
  • Note that this is in no way guaranteed to work, since the language does not specify what function.toString actually does, and it will be outright wrong when it actually refers to variables from its own surrounding scope. – Andreas Rossberg Mar 24 '13 at 15:22
  • @AndreasRossberg - I've never seen an actual interpreter which doesn't execute this code properly. I've tested this code in Rhino, SpiderMonkey and V8 and they all return the same results. Also I didn't understand what you meant by _"it will be outright wrong when it actually refers to variables from its own surrounding scope"_. Care to explain properly? With an example perhaps. – Aadit M Shah Mar 24 '13 at 16:23
  • @AaditMShah, perhaps, but it's still an implementation-dependent extension, and the language as specified does not support it. Regarding the other question, consider e.g. `function f(y) { var x; function g() { x += y }; return g } var g1 = f(1); function h() { var x = 0; eval("(" + g1 + ")()"); return x } h();` -- this will throw a ReferenceError for `y`. The point being that string conversion cannot deal with closures, and unless your intention is to override _every_ free variable in a function, it won't do what you want. – Andreas Rossberg Mar 24 '13 at 18:15
  • @AndreasRossberg I believe in this particular case the intention is to bind every free variable. That's what dynamic scoping is all about isn't it? Free variables are mapped to the context of the scope of the caller instead of the lexical scope of the callee. AFAIK declaring only some free variables as dynamically scoped is only possible in some variants of LISP. Your example code wouldn't even work in bash which only has dynamic scoping, and it's because it's invalid code. You can't mix dynamic and lexical scoping. The point is that the specs leave it undefined, but my code works everywhere. – Aadit M Shah Mar 25 '13 at 03:15
  • @AaditMShah, well, IMHO, any _sane_ implementation of dynamic scoping has to allow combining it with static scoping. But be that as it may be, here is another way in which your suggestion does not generally work, going back to Bergi's comment elsewhere: try it on `function a() { if (a.mode === "inc) ++x; else if (a.mode === "dec") --x; else throw "panic" } a.mode = "inc";`. The moral is that a function is more than just a piece of text, and so stringification can hardly ever be more than a brittle hack. – Andreas Rossberg Mar 25 '13 at 08:16
  • @AndreasRossberg - That isn't even a valid program. Your string literal is unterminated. Plus I don't see what you're hinting at. The only case where dynamic scoping won't work is when dealing with native functions, but since you don't know the free variables of a native function anyway there no need to use dynamic scoping with native functions. The point is that in JavaScript user defined functions are just a piece of text which is why string conversion works. Also combining lexical and dynamic scoping is a really bad idea which is why the LISP community frowns upon it just like `eval` in JS. – Aadit M Shah Mar 25 '13 at 09:25
  • 1
    @AaditMShah, yeah, sorry for the missing `"`, but come on, that's a trifle. I don't understand how you can still claim that JavaScript functions are just a piece of text when I showed two counter examples clearly demonstrating the opposite. Functions are _objects_ that carry _state_, both in their public properties and in their private closure environment. Neither is preserved by stringification. Would you claim that other objects are just a piece of text? – Andreas Rossberg Mar 25 '13 at 10:27
  • @AndreasRossberg - Ah you'll definitely lose the properties declared on the function itself. That can't be remedied. However a dynamically scoped function is not supposed to carry its lexical environment with it. It's a trade-off. The only thing you lose is the properties declared on the function. – Aadit M Shah Mar 25 '13 at 11:13
  • 1
    *I've never seen an actual interpreter which doesn't execute this code properly.* That's what people said about ordered props for a decade, then V8 came and exploited the fact in the spec that props don't have to be ordered to gain performance boosts. Then other browsers of course followed and there was [a huge amount of people asking for ordered properties again](https://code.google.com/p/v8/issues/detail?id=164). Those developers were thinking like you: "since it works this way in everything I tried, it must be this way". But actually it is just a coincidence and history could repeat itself. – Esailija Aug 01 '13 at 11:46
  • 1
    In this particular case though it seems that it will be specified in ES6... just saying though that if that wasn't the case then this attitude can be harmful :p – Esailija Aug 01 '13 at 11:56
1

No, there isn't. And that's definitely a feature.

Update

Since some answers and comments around here suggested using function.toString and eval to emulate dynamic scoping, let me summarize why I think that that is not a proper solution (besides being a bad idea anyways):

  1. The result of invoking toString on functions is not specified by the language standard, nor intended for this kind of use. It happens to work on most current browsers for many (user-defined) functions, but there is no guarantee for that, and I strongly advise against writing production code that relies on it.

  2. Functions are far more than the piece of text that stringification can give you. In particular, functions are objects that have identity and carry state, neither of which stringification can preserve. More precisely:

    • Functions can have properties that the function body accesses (in particular, the prototype property). They will be lost across stringification.

    • Functions have a closure environment encapsulating the variables from surrounding scope they use. Again, this will be lost, resulting in dangling or misbound variable references (even if that perhaps was the purpose of the exercise for some of them).

    • Functions have other potentially relevant properties, like their identity, a modified __proto__, or configuration states like being non-extensible. None of that is preserved.

    • Not all functions are simple JavaScript source functions. They may e.g. be native, or from ES6 on, they may be function proxies. In neither case will stringification be useful.

  3. A function with unbound variable references is not legal in strict mode (and thus will be illegal in many places in ES6).

So while stringification may work in some simple cases, that is by accident, not by design. It is a brittle hack. Steer clear of it.

Andreas Rossberg
  • 34,518
  • 3
  • 61
  • 72
  • 1
    +1, though your second statement is debatable. Dynamic scoping is a feature as well :-) – Bergi Mar 24 '13 at 16:19
  • I don't see how you can claim to state that there isn't any way to do what the OP wants. Especially after you read my answer. Did you see the demo? http://jsfiddle.net/ucGxD/ Please elaborate. – Aadit M Shah Mar 24 '13 at 16:26
  • @AaditMShah: That doesn't really *modify* the ([[scope]] of) function `a`, but decompiles it and evals the code to get a *new* function in a different scope. – Bergi Mar 24 '13 at 17:00
  • @AaditMShah, see my reply to your answer. Also, what Bergi said. – Andreas Rossberg Mar 24 '13 at 18:17
  • @Bergi - I never said that it modifies the scope of function `a` and I know that it creates a new function from the blueprint of `a` in the scope of `b`. That however is not what the OP asked is it? The OP asked _"is there a way to call `a` inside `b`, forcing it to use `b`'s scope?"_ and my answer precisely addresses that issue. Andreas OTOH says that it's not possible at all. I disagree and my answer proves it. – Aadit M Shah Mar 25 '13 at 03:00
  • 1
    @AaditMShah: well, to be pedantic, and as Bergi points out, you are not "calling a". You are calling a different function also named a. Which is observable if you were to compare their identity. The rest of the disagreement is about what defines "JavaScript". For me, its what the standard says, not historic incidence, no matter how common. – Andreas Rossberg Mar 25 '13 at 07:44
  • @AndreasRossberg - Fair enough. I wonder where did the OP scamper off to. – Aadit M Shah Mar 25 '13 at 08:00
  • 1
    Updated my answer to reflect my comments. – Andreas Rossberg Mar 25 '13 at 10:51
  • @AaditMShah: All right, I referred to the title question about "*modify a closure of a function*" - which is indeed impossible. Not sure who the downvoter is, but the updated post only deserves upvotes! – Bergi Mar 25 '13 at 14:41
  • @AndreasRossberg - I did some digging and found out that the `toString` method is indeed defined for functions in the specs: http://es5.github.com/#x15.3.4.2 It says _"An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. Note in particular that the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent."_ This effectively makes your first point null and void. – Aadit M Shah Mar 25 '13 at 15:07
  • @AaditMShah, `"function(){}"` is a conforming implementation. The snippet you quote does not mean more, because in the general case, an implementation cannot be more accurate (for the reasons I list). Also, FWIW, all implementations I'm aware of violate even this minimal requirement in many cases (try e.g. `[].push.toString()` on the platform of your choice -- the result is not valid syntax). Having said all that, you may be pleased to hear that there is an effort to specify `fn.toString` more thoroughly for ES6. – Andreas Rossberg Mar 25 '13 at 19:35
-1

Yes. Context can be transferred by call or apply. You can set the context to scope by calling bind to itself. See this example:

function a() {
    return ++this.x;
}

function b() {
    this.x = this.x || 0;
    return a.call(this);  // a works with x in b's context
}

b = b.bind(b); // now the b's context is the b's scope :-)

console.log(b(),b(),b()); // 1 2 3
console.log(x); // error: x is not defined - global scope is clear

As Quentin points out see the difference between context and scope here.

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169