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?

4

6 に答える 6

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());
})();
于 2013-03-24T13:20:07.060 に答える
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.

于 2013-03-24T13:19:43.560 に答える
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.

于 2013-03-24T13:45:32.523 に答える
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/

于 2013-03-24T14:07:38.637 に答える
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.

于 2013-03-24T15:19:24.273 に答える
-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.

于 2013-03-24T14:18:25.890 に答える