JavaScript函数作用域及闭包

在JS中我认为最难理解的地方莫过于函数作用域与闭包了。《JavaScript权威指南》关于这部分的章节看了N遍,也没有领会。这次开发中的一个小小意外,倒是让我受益不小。下面我们用例子逐渐深入来讨论一下这方面的问题。

发生意外的代码:

var arr = [];
for(var i = 0; i<5; i++)
    arr[i] = function()(return i); 
for(var j = 0; j<arr.length; j++) {
    alert(arr[j]);
    alert(arr[j]());

//用i做循环试试

//for(var i = 0; i<5; i++)
//    alert(arr[i]);
//    alert(arr[i]());
}	

上面代码发生了什么我们先不去解释,下面逐渐深入:

var i = 100; 
var f=function(i){return i};

alert(f('Hello'));  //显示"Hello"

这很正常,既然进来看这篇文章就肯定能看懂

var i = 100;

var f = function(){return i;};

alert(f()); //显示100

i="hello";

alert(f());  //显示"hello"

解释:函数f定义在全局环境(global)中,那么他的环境链(scope chain)就是“call对象-->global对象”。因为在它自己的局部范围内(局部变量及参数)没有关于i的定义,所以他就沿着scope chain向上找,直到在全局环境中找到并返回了这个值。回头看第一个例子,之所以返回“Hello”而不是全局环境中定义的100是因为函数自己有i的定义(参数),不用再向上找。我们得出结论:在scope chain中“近处”的变量会覆盖掉“远处”的相同变量。

var f = (function(i){return function(){return i;}})(100) //f = function(){return i;}

var i = "hello";

alert(f()); //显示100

解释:第一条语句执行后f = function(){return i;} ,不信你可以用alert(f)看看。f函数自己没有关于i的定义。第二句定义了一个全局变量i="Hello"。既然f中没有定义i,为什么没有像上面的例子那样返回全局环境中的i?原因是f是一个嵌套函数,他是在另一个函数中定义的。此时他的scope chain是"call对象(f)-->call对象(外层函数)-->global对象。在f被调用时,沿着这个链找,他在外层函数中找到了i(外层函数的参数,值为100).他覆盖了全局环境中的i("hello"). 你可能会问在调用f的时候外层函数早就结束调用了(要不然怎么返回f)。那么他的实际参数(i=100)应该失效被释放掉了,怎么f还能找到呢?这就是“闭包”了。因为嵌套函数被返回并由f引用,作为他的scope chain的一部分的外层函数的call对象得不到释放(call对象保存了所有的局部变量及参数信息)。

现在看开始那段让我发生意外的代码就知道原因了:在全局环境的for循环中定义了5个函数(这里表述有点问题,稍后会讲)。循环体执行完成后i=5,然后分别调用这5个函数,得到的值都为5,因为这5个函数自己没有i,只能在全局环境中找。如果你用i做循环数来调用这5个函数得到的值依次为:0,1,2....4。这是因为循环过程中i被重新赋值了。

下面再看一段代码:

var arr = [];

for(var i = 0; i<5; i++) 

arr[i] = new Function("return " + i); 

for(var j = 0; j<arr.length; j++)

alert(arr[j]); alert(arr[j]());

用alert查看返回的函数体你会发现每次返回的是不同的函数。而且i值被替换掉了。每调用一次Function就返回一个新函数。而用function常量定义的函数都一样(所以前面说表述有问题)。下面是《Javascript权威指南》中作者的原话:

The Function() constructor parses the function body and creates a new function object each time it is called. If the call to the constructor appears within a loop or within a frequently called function, this process can be inefficient. By contrast, a function literal or nested function that appears within a loop or function is not recompiled each time it is encountered. Nor is a different function object created each time a function literal is encountered.

另外关于Function()需要注意的一点是,用Function()创建的函数永远是顶层函数。所以用Function()创建的函数不存在闭包问题。下面的代码就是解释:

var f = (function(i){return new Function("return i")})(100);


var i = "hello";

alert(f);

alert(f()); 

好了,上面就是我所理解的关于函数作用域及闭包的全部。如果存在错误,还请大家提出来。谢谢。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值