从回调和内联自调用函数访问父函数变量

时间:2014-06-08 05:24:07

标签: javascript

我相信首先看两个自调用函数是一样的,它们之间的区别只是在第一个我传递回调函数然后通过参数对象执行而在第二个做同样的事情通过使函数自我调用。现在来到访问父变量的点,在第一个示例名称为“undefined”,而在第二个示例中,它是可访问的并且输出“Nishant”我无法弄清楚它是如何工作的!

(function(){
    var name = "Nishant";
    arguments[0]();

})(function(){console.log(name);});

输出:(空字符串)

(function(){
  var name = "Nishant";
  (function(){console.log(name);})()
})();

输出:Nishant

2 个答案:

答案 0 :(得分:2)

  

我无法弄清楚它是如何工作的!

JavaScript有lexical scope。这意味着范围取决于在源代码中定义函数的位置(与动态范围不同,其中范围是在运行时调用函数时确定的。)

让我们为您的函数添加一些名称,以便我们更容易地引用它们:

(function foo(){
    var name = "Nishant";
    arguments[0]();
})(function bar(){console.log(name);});

在这种情况下,bar在<{1}}的之外定义,因此无法访问foo内定义的变量。事实上,在创建foo时,bar内的变量name甚至还不存在,因为foo尚未执行。
当你没有内联定义时,可能会更容易看到:

foo

这看起来可能比较熟悉,我打赌你不会指望function bar(){console.log(name);} (function foo(){ var name = "Nishant"; arguments[0](); })(bar); 内的namebar内的name有任何关系,对吗?


在另一种情况下,

foo

您在(function foo(){ var name = "Nishant"; (function bar(){console.log(name);})() })(); 中定义了bar foo也位于name内,因此foo可以访问该变量(词法范围+闭包)。

答案 1 :(得分:1)

&#34;&#34;名称&#34;在第一个版本中引用全局window.name属性*,在第二个示例中引用通过闭包的私有var名称。 window.name属性是可选的,默认为空字符串。

如果此属性不存在,您的第一个示例将引发引用错误。

闭包是在声明函数的范围内创建的,而不是在它被调用的位置创建的,在第一个例子中,函数是在全局范围内声明的,在包装函数范围内的第二个中。

window.name = "NoClosure";
(function(){
    var name = "Closure";
    arguments[0](); 
})(function(){console.log(name);}); //>"NoClosure"

window.name = "NoClosure";
(function(){
    var name = "Closure";
    (function(){console.log(name);})(); //>"Closure"
})();

如果您使用console.dir()检查回调,您可以看到它没有关闭,如果您想自己测试它,这里有一个片段。

(function wrapper(){
    var isPrivate = "A closure could see me";
    console.dir(arguments[0]);
})(function callback(){});

这是另一个带有2个闭包的实验设置,显示在对函数的调用中声明的函数能够获得闭包,但它总是关闭函数范围传递它,它无法访问作为参数传递给的函数的范围,并且稍后将其称为回调。

(function outerScope(){
   var outerVar = "in outer scope";
   (function innerScope(cb){
       var innerVar = "in inner scope";
       cb();
   })(function callback(){
       console.log(
          "outer:"+ typeof outerVar,
          "inner:"+ typeof innerVar);
       console.dir(arguments.callee);
   })
})()

*来源https://developer.mozilla.org/en-US/docs/Web/API/Window.name