闭包与变量问题

想了一晚上,哥们儿讲的太好了,终于让我搞懂了

写早了,其实并没有搞懂

许多人第一次接触闭包大概都是从高程里这段代码开始的:

function createFunctions() {
    var result = new Array();
    for(var i=0; i<10; i++) {
        result[i] = function() {
            return i;
        }
    }
    return result;
}
var foo = createFunction();

 或者是用for循环在给网页中一连串元素绑定例如onclick事件时。

所有的教材在讲到这一点时都会给出这样的解释: 因为每个函数都保存着createFunction中的活动对象,所以它们引用的都是同一个变量 i 。而循环结束后 i 的值为10,所以每个函数的输出都是10.

解释非常简洁与正确。

然而还是会有一部分人看了这个解释后一知半解,比如我。

我第一次看到这个解释后有了这么一连串疑问: 虽然知道 i 最终是 10,但是在每次赋值过程中 i 并不是 10 啊,为什么非要取最后一个值呢?i 并不是引用数据类型,为什么可以说“它们引用的都是同一个变量 i ?

如果你和我一样有这个疑问,其实对这个问题而言我们不理解的地方并不是闭包,但是这个问题被打上了一个严重的”闭包“标签,导致很长一段时间里我都以为自己不了解闭包。

实际上,我不理解的并不是闭包这个概念,而是更为基础的,函数调用的时机。

 

我们把代码中赋值的哪一段改一下:

result[i] = function() { return j; }

 把 i 改成 j, 一个并没有定义的变量。

如果我们仅仅把改完之后的代码贴到console里运行,它是不会报错的。因为虽然createFunctions被调用了,却并未调用赋给result的函数。

只有继续使用语句调用result中的某个元素:

result[0](1);
 这样才会抛出 undefined 错误。

这说明了一个问题:仅仅声明某一个函数,引擎并不会对函数内部的任何变量进行查找或赋值操作。只会对函数内部的语法错误进行检查(如果往内部函数加上非法语句,那么不用调用也会报错)
(这句说的太好了,指点了我的迷津!!!)。
 
所以开头问题里的循环语句:

for(var i=0; i<10; i++) 
    result[i] = function() 
        return i;

 我原本以为它是这样的:

 
result[0] = function() { return 0; };
 result[1] = function() { return 1; };
 result[2] = function() { return 2; };

 实际上它是这样的:

 
result[0] = function() { return i; };
 result[1] = function() { return i; };
 result[2] = function() { return i; };

 数组里的 i 和 函数里的 i 并不是一回事, 外面的是常量, 里面的是变量。

而当我们调用result[0]函数时, 这个函数执行到 return 语句,发现并没有 i 这个变量,于是顺着作用链去找,在createFunctions里找到了已经变成10的 i ,于是输出 10. 这个过程才是闭包的寻找变量的过程。

===========================================================

但是其实i也是在变化着的,依托着上一次的输出的result【i-1】,因为闭包所保存的是整个变量对象,而不是单纯的某个变量,在这里它保存着的是对象i,而不是单独某一个时刻的i的值;对象是引用变量类型,也就是说它指向某一个地址,因此只在最后调用的时候才显示他的值。(这是我自己的理解)

==============================================================

根据这个思路寻找解决方案时思路就明确多了,只要在每次赋值过程中,不让 i 作为变量,而是确确实实地利用当时 i 的值,方法就是将 i 作为函数参数进行调用:

result[i] = (function(val) { return val; })(i);

 这样一来在每一次赋值的过程中,每一个result[i]都与 i 的当前值产生了联系。

当然,这样修改的问题在于,原题返回的是一个函数,这里返回的却是一个值。

所以还要把返回值改成相应的函数:

result[i] = (function (val) {
   return function () {
     return val;
   };
 })(i);

 这样相当于给目标函数套上了一层块级作用域,并且在 i 每次循环时都将它的值赋给了这个块级作用域中的一个临时变量。这个临时变量其实和 i 没有太大区别,只不过 i 在它的作用域声明时值为 0 ,结束后变成了10.而对每个临时变量而言,开始是多少,结束还是多少。

 

进一步谈闭包

任何声明在另一个函数内部的函数都可以称为闭包。也就是说,闭包是一个函数。不过也有些地方会讲闭包是内部函数以及其作用域链组成的一个整体。两种说法其实一个意思,毕竟严格来说,函数的作用域也是函数的一部分。不过我更喜欢后面一种说法,因为它强调了闭包的重点:维持作用域。

闭包主要有两个概念:可以访问外部函数,维持函数作用域。第一个概念并没有什么特别,大部分编程语言都有这个特性,内部函数可以访问其外部变量这种事情很常见。所以重点在于第二点。举例如下:

var globalValue;

function out() {
    var value = 1;
    function inner() {
        return value;
    }
    globalValue = inner;
}

out();

globalValue() // return 1;

 我们先不考虑闭包地看一下这个问题:首先声明了一个全局变量,然后调用了out函数,调用函数的过程中全局变量被赋值了一个函数。out函数调用结束之后,按照内存处理机制,它内部的所有变量应该都被释放掉了,不过还好我们把inner复制给了全局变量,所以还可以在外部调用它。接下来我们调用了全局变量,这时候因为out内部作用域已经被释放了,所以应该找不到value的值,返回应该是undefined。

但是事实是,它的确返回了 1,即内部变量。本该已经消失了,只能存在于out函数内部的变量,走到了墙外。这就是闭包的强大之处。

 

补充一些:

function CreatFunction(){
	var result=[];
	// var j=5;
	for (var i =0; i <10; i++) {
	result[i]=function() {
            return i;
        };
    result[0]();
	   }
    
return result;

}
CreatFunction()

这是i=0时,闭包的情况在这里插入图片描述
这是i=2时,闭包的情况
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值