简体   繁体   中英

Access local variable inside a callback function

var inner = function() { console.log(x); }

// test 1
(function(cb) { var x = 123; cb(); })(inner);

// test 2
(function(cb) { var x = 123; cb.apply(this); })(inner);

// test 3
(function(cb) { var x = 123; cb.bind(this)(); })(inner);

// test 4
(function(cb) { cb.bind({x: 123})(); })(inner);

All tests result in: ReferenceError: x is not defined

Do someone know how it is possible to access 'x' as a local variable inside the callback?

Fact: when you do var inner = function() { console.log(x); } var inner = function() { console.log(x); } in your first line, x is not defined. Why? Because, inside your inner function, there's no local declaration of x (which would be done with var x = something ). The runtime will then look up in the next scope, that is the global scope. There isn't, also, a declaration of x , so x is also not defined there.

The only places where there is a variable called x are inside each one of your 4 IIFEs following. But inside the IIFEs, each x is a different variable, in a different scope. So, if what you want is to console.log() the x defined inside each IIFE, you are taking the wrong approach.

Keep in mind that, when you define inner , you are capturing the environment inside the function's closure. It means that, whatever value could x have there (in the declaration of the function), would be the available value to the x variable later, when the inner function would be used. The fact that your x there is not defined is only an accessory, and is not what is causing the undesired behavior.

So, what happens is that when you call your inner function inside any of your IIFEs, the x referred to inside the inner function declaration is a captured value of what x had as a value when the function was defined , not the value that x has now in the scope where the function is currently being called . This is what is called lexical scope .

To solve this, you would have to pass the value that you want to console.log() inside the inner function as a parameter to the inner function, as so:

var inner = function(x) { console.log(x); }
// test 1
(function(cb) { var x = 123; cb(x); })(inner);

The only way to access the local variable x in the callback, is to pass it as an argument:

var inner = function(some_var) { console.log(some_var); }; //logs 123
(function(cb) { var x = 123; cb(x); })(inner);

OR

var inner = function(some_var) { console.log(some_var); }; //logs 123
(function(cb) { var x = 123; cb.apply(this,[x]); })(inner);

OR

var inner = function(some_var) { console.log(some_var); }; //logs 123
(function(cb) { var x = 123; cb.call(this,x); })(inner);

FURTHER

Because JS is lexically scoped, trying to reference the local variable after the anonymous function has finished executing is impossible by any other means. If you don't pass it as an argument to make it available elsewhere, JS will see it as non-reachable and it will be eligible for garbage collection.

You could redefine the callback function in the current scope:

var inner = function() { console.log(x); }

(function(cb) { var x = 123; eval('cb = ' + cb.toString()); cb(); })(inner);

// or

(function(cb) { var x = 123; eval('(' + cb.toString() + ')')(); })(inner);

This will not work if the function relies on anything in the scope in which it was originally defined or if the Javascript file has been minified. The use of eval may introduce security, performance, and code quality issues.

Have you tried using events? Emit an event inside the anonymous function, then subscribe to it in your own function somewhere else that carries out your logic.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM