js和C#中都有闭包的概念,闭包本质上是一个对象,是指有权访问另一个局部作用域中的变量的对象(或函数,在C#中是委托)。这个对象和函数/方法有关:

在js中,闭包是由于函数引用了局部变量形成的。在C#中,是由于匿名函数(本质上是委托)的存在而产生的和js原理差不多的闭包。

要了解js中的闭包,先要了解几个概念:

作用域链:而有关如何创建作用域链以及作用域链有什么作用的细节, 对彻底理解闭包至关重要。 当某 个函数第一次被调用时,会创建一个执行环境(execution context)及相应的作用域 链,并把作用域链赋值给 一个 特殊的内部属性( 即[[ Scope]])。 然后, 使用 this、 arguments 和 其他命名参数的值来初始化函数的活动对象( activation object)。 但在作用域链中, 外部函数的活动对象始终处于第二位, 外部函数的外部函数的活动对象处于第三位,…… 直至作为作用域链终点的全局执行环境。

在函数执行过程中, 为读取和写入变量的值, 就需要在作用域 链中查找变量。 来看下面的例子。

function compare( value1, value2){
if (value1 < value2){
return -1;
}
else if (value1 > value2){
return 1;
} else { return 0;
}
}
var result = compare( 5, 10);

以上代码先创建了compare函数,又在去全局作用域中调用了它。当第一次调用它的时候,会创建一个包含this、arguments、value1、value2、的活动对象。全局执行环境的变量对象(有this、result和compare)则处于compare执行环境中作用域链上的第二位。

后台的每个执行环境都有 一个表示变量的对象—— 变量对象。 全局环境 的变量对象始终存在,而像 compare() 函数这样的局部环境的变量对象, 则只在函数执行的过程中存在。 在创建compare() 函数时, 会创建一个预先包含全局变量对象的作用域链, 这个作用域链 被保存在内部的[[ Scope]] 属性中。 当调用 compare() 函数时, 会为函数创建一个执行环境, 然后通过复制函数的[[ Scope]] 属性中的对象构建起执行 环境的作用域链。 此后, 又有 一个活动对象( 在此作为变量对象使用) 被创建并被推入执行 环境作用域链 的前端。 对于这个例子中compare() 函数的执行环境而言, 其作用域链 中包含两个变量对象: 本地活动对象和全局变量对象。 显然, 作用域链本质上是一个指向变量对象的指针列表, 它只引用但不实际包含变量对象。 无论什么时候在函数中访问一个变量时, 就会从作用域链 中搜索具有相应名字的变量。 一般来讲, 当函数执行完毕后, 局部活动对象就会被销毁, 内存中仅保存全局作用域( 全局执行环境的变量对象)。 但是, 闭包的情况又有所不同。

在另 一个函数内部定义的函数会将包含函数( 即外部函数) 的活动对象添加到它的作用域链中。这样,在内部函数中的作用域链上边保存了包含环境中的活动对象(或者叫变量对象),那么,为了能够安全的执行内部函数,外部函数的变量对象会知道内部函数执行完毕后才会被安全的销毁。

需要注意的一点是闭包只有在被真正的调用时才会执行,这也引发了另外一个本质的东西,就是闭包只会调用同一个外部变量上面的最后一个值:

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

上面这个代码段中的循环变量i实际上时被当作局部变量而不是块级作用域下面的一个变量,因为js没有块级作用域,同样的道理在C#中也是,C#中的循环变量也是被当成局部变量的。回到正题:

result[i]=function(){return i;}这句会形成一个闭包,当我们调用createFunctions这个函数的时候会返回这个闭包:

var arr=createFunctions();
arr这个变量引用createFunction函数的返回值,是一个函数数组,每一个数组的元素都是function(){return i;}.当查看这个数组中的元素时:console.log(arr[0]());发现不管吧arr下标改成多少都是显示的10。

这是因为每个函数的作用域链中都保存着 createFunctions() 函数的活动对象, 所以它们引用的都是同一个变量 i。 当createFunctions() 函数返回后, 变量 i 的值是 10, 此时每个函数都引用着保存变量 i 的同一个变量 对象, 所以在每个函数内部i的值都是 10。

修正这个情况的办法是让闭包立即执行:

function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function (num) {
return function () {
return num;
};
}(i);
} return result;
} var result = createFunctions();
console.log(result[1]());

在 重写 了 前面 的 createFunctions() 函数 后, 每个 函数 就会 返回 各自 不同 的 索引 值了。 在这 个 版本 中, 我们 没有 直接 把 闭 包 赋值 给 数组, 而是 定义 了 一个 匿名 函数, 并将 立即 执行 该 匿名 函数 的 结果 赋 给 数组。 这里 的 匿名 函数 有一个 参数 num, 也就是 最终 的 函数 要 返回 的 值。 在调 用 每个 匿名 函数 时, 我们 传入 了 变量 i。 由于 函数 参数 是按 值 传递 的, 所以 就会 将 变量 i 的 当前 值 复制 给 参数 num。 而在 这个 匿名 函数 内部, 又 创建 并 返回 了 一个 访问 num 的 闭 包。 这样一来, result 数组 中的 每个 函数 都有 自己 num 变量 的 一个 副本, 因此 就可以 返回 各自 不同 的 数值 了。

在C#中解决这个问题的办法是在块级作用域中加入一个更内部层次的作用域的变量来保存循环变量的值,这样,在闭包背后生成的类会处于块级作用域中,每次循环时都会产生一个类来保存这个值,也就是说这个值都是最新的。

 class Program
{
static void Main(string[] args)
{
var funcList= ReturnFunc();
foreach (var item in funcList)
{
Console.WriteLine(item());
}
Console.ReadKey();
} static List<Func<int>> ReturnFunc()
{
List<Func<int>> list = new List<Func<int>>();
for (int i = ; i < ; i++)
{
var num = i;//就是这里
list.Add(()=> num); }
return list;
}
}

Javascript中的闭包和C#中的闭包的更多相关文章

  1. [转]JavaScript中的匿名函数及函数的闭包

    JavaScript中的匿名函数及函数的闭包  原文地址:http://www.cnblogs.com/wl0000-03/p/6050108.html 1.匿名函数 函数是JavaScript中最灵 ...

  2. JavaScript中的匿名函数及函数的闭包(转)

    JavaScript中的匿名函数及函数的闭包  https://www.cnblogs.com/wl0000-03/p/6050108.html 1.匿名函数 函数是JavaScript中最灵活的一种 ...

  3. 深入理解JavaScript中的作用域、作用域链和闭包

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qappleh/article/detai ...

  4. 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?

    在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...

  5. jQuery中的闭包和js中的闭包总结

    关于闭包的知识总结下: 一.闭包 1.定义 闭包的关键是作用域,概念是:能有读取其他函数内部的函数 使用的场景有很多,最常见的是函数封装的时候,再就是在使用定时器的时候,会经常用到; //闭包:有参数 ...

  6. 闭包在python中的应用,translate和maketrans方法详解

    python对字符串的处理是比较高效的,方法很多.maketrans和translate两个方法被应用的很多,但是具体怎么用常常想不起来. 让我们先回顾下这两个方法吧: 1.s.translate(t ...

  7. javascript随机将第一个dom中的图片添加到第二个div中去

    javascript随机将第一个dom中的图片添加到第二个div中去,此代码的是一个简单的例子,将第一个div中的五张图片中,提取随机两张显示到第二个div中. <!DOCTYPE html P ...

  8. JavaScript向select下拉框中加入和删除元素

    JavaScript向select下拉框中加入和删除元素 1.说明 a   利用append()方法向下拉框中加入元素 b   利用remove()方法移除下拉框中最后一个元素 2.设计源代码 < ...

  9. javascript 的Date 格式化, 模仿shell中date命令的格式

    原文:javascript 的Date 格式化, 模仿shell中date命令的格式 shell 中显示当前的日期 [root@localhost]$ date '+%Y-%m-%d %H:%M:%S ...

随机推荐

  1. dd测试

    time dd if=/dev/zero of=/root/test.db2 bs=200K count=10000 oflag=dsync

  2. 【spring-boot神器】第一篇:拦截器,过滤器,监听器,控制器,消息转换器,AOP执行顺序

    整理一下这几天学习的资料和代码 第一部分.上代码 1.spring各种器的实现,idea搭建spring-boot的教程在这里http://www.jianshu.com/p/9082a533fa3c ...

  3. P1823 [COI2007] Patrik 音乐会的等待 单调栈 洛谷luogu

    题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的. ...

  4. javascript 深度克隆对象

    js一般有两种不同数据类型的值: 基本类型(包括undefined,Null,boolean,String,Number),按值传递: 引用类型(包括数组,对象),按址传递,引用类型在值传递的时候是内 ...

  5. PAT A1129 Recommendation System (25 分)——set,结构体重载小于号

    Recommendation system predicts the preference that a user would give to an item. Now you are asked t ...

  6. 【Codeforces 526D】Om Nom and Necklace

    Codeforces 526 D 题意:给一个字符串,求每个前缀是否能表示成\(A+B+A+B+\dots+A\)(\(k\)个\(A+B\))的形式. 思路1:求出所有前缀的哈希值,以便求每个子串的 ...

  7. node通过http.request向其他服务器上传文件

    function upload(callback) { let boundaryKey = '----' + new Date().getTime(); // 用于标识请求数据段 let option ...

  8. 学习Android(入门基础和实用教程)

    为了方便大家学习,准备录制Android基础篇的视频教程, https://item.taobao.com/item.htm?spm=0.7095261.0.0.17a61debAKIDPI& ...

  9. Mac安装LNMP环境,升级php7

    Mac安装nginx+mysql+php 安装nginx比较麻烦,要安装pcre       ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre ...

  10. How to Enable TLS 1.2 on Windows Server 2008 R2 and IIS 7.5

    Nowadays there is an SSL vulnerability called POODLE discovered by Google team in SSLv3 protocol. So ...