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);
所有测试都会导致: ReferenceError:x未定义
有人知道如何在回调中访问'x'作为局部变量吗?
答案 0 :(得分:10)
事实:当您在第一行中var inner = function() { console.log(x); }
时,x
未定义。为什么?因为,在inner
函数中,没有x的本地声明(可以使用var x = something
完成)。然后,运行时将在下一个范围中查找,即全局范围。还没有x
的声明,因此x
也没有定义。
只有名为x
的变量的地方位于您的4个IIFE中的每一个之后。但在IIFE内部,每个x
是一个不同的变量,在不同的范围内。所以,如果你想要的是console.log()
每个IIFE中定义的x
,你就采取了错误的方法。
请记住,当您定义inner
时,您正在捕获函数关闭内的环境。这意味着,无论x
有什么值(在函数的声明中),都将是x
变量的可用值,稍后将使用inner
函数。您的x
未定义的事实只是一个附件,而不是导致不良行为的原因。
所以,当您在任何IIFE中调用inner
函数时,x
函数声明中引用的inner
是x
的捕获值x
1}}在定义函数时具有值,而不是console.log()
现在在函数当前被称为的范围内的值。这就是所谓的词法范围。
要解决此问题,您必须将inner
函数内的inner
值作为参数传递给var inner = function(x) { console.log(x); }
// test 1
(function(cb) { var x = 123; cb(x); })(inner);
函数,如下所示:
{{1}}
答案 1 :(得分:3)
在回调中访问本地变量x
的唯一方法是将其作为参数传递:
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);
<强>继续强>
因为JS是词法范围的,所以在匿名函数执行完毕后尝试引用局部变量是不可能的。如果你没有将它作为参数传递给其他地方使用,那么JS会将其视为不可访问的,并且它有资格进行垃圾收集。
答案 2 :(得分:0)
您可以在当前范围内重新定义回调函数:
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);
如果函数依赖于最初定义范围内的任何内容或者Javascript文件已缩小,则无效。 eval
的使用可能会带来安全性,性能和代码质量问题。
答案 3 :(得分:0)
您是否尝试过使用事件?在匿名函数内发出一个事件,然后在执行逻辑的其他地方的自己的函数中订阅该事件。