js中的闭包和c#中的闭包

2021-04-22 17:26

阅读:517

 

我们 

关于闭包,一个老僧长谈的话题;js的闭包俺将的比较多了,而且很详细,俺就不说了,可以看看之前的文章;

我们来对比一下c#中的闭包和js中的闭包;

先看我们的c#代码;

        static List fn0()
        {
            int result = 0;
            List list = new List();
            for (int i = 0; i 10; i++)
            {
                result = i + 1; //这样result相当于一个全局变量
                Action action = () =>
                {
                    Console.WriteLine(result);
                };
                list.Add(action);
            }
            return list;
        }

        static List fn1()
        {
            
            List list = new List();
            for (int i = 0; i 10; i++)
            {
               int result = i + 1; //这样相当于一个局部变量; for 循环内部的变量;
                Action action = () =>
                {
                    Console.WriteLine(result);
                };
                list.Add(action);
            }
            return list;
        }

        static void Main(string[] args)
        {

            //这样看到了,结果全尼玛的是十滴呀;
            fn0().ForEach(o=>o());
            Console.WriteLine("------------------"); //闭包的问题会在我们的for循环和多线程中出现id呀;效果不一般滴呀;
            fn1().ForEach(o => o());
         }

结果可想而知;

技术分享图片

 

那么对于,第二种方式,如果我们使用js代码来实现呢;(ps:js的数组可以直接存我们的函数,ps:js中函数就是对象,所以就可以存)

function show(){
 var arr=[];
 for(var i=0;i){
      var result=i+1;
     arr.push(function (){
         console.log(result);
        
     })
      
  }
  return arr;
};
var list=show();

  for(var i=0;i    list[i]();
  }


结果:

技术分享图片

 

 出现这样的原因很简单啦,俺都不想再重复,因为js中没有块级作用域的概念;不不不不,这样说不太正确,具体的应该是在esx版本前没有,具体的俺也记不清楚了;

解决方法有很多呀;形成闭包啊,使用let 关键字呀;

让我们来看看闭包是如何形成的;

function show(){
 var arr=[];
 for(var i=0;i){
      var result=i+1;
     arr.push(function (index){
             return function (){
                console.log(index);
             }
        
     }(result))
      
  }
  return arr;
};
var list=show();
for(var i=0;i){
    list[i]();
}

 

它借助了我们的function(最外面的function)形成了一个新的作用域(result=index),这样i=>result=>index就形成了,我们的一个独立的块级别多用于了滴呀;

方法还挺巧妙的,但是对于新的es,一个let就可以解决问题了;

总结:问题的本质:js中没有块级别作用域,通过function形成一个新的额块级别作用域;这样innerfunction 访问outerfunction的变量(参数);就形成了我们的闭包;

闭包的定义之一:这里我强调的是定义之一://函数内部可以访问函数所在的作用域;,当然它有一个明显的问题就是:内存泄漏;


再回过头来看看我们c#中代码,以为c#中是有块级别作用域的,所以,方法二就没有问题;

 

我们看看;维基百科上定义:

闭包:词法闭包;是引用了自由变量的函数;这个被引用的自由变量,和这个函数一同存在,即是已经离开了创造它的环境;所以。有另一种说法认为:

闭包是由函数和与其相关的引用环境组合成的实体;

 

var x = 1;
Action action = () =>
{
 var y = 2;
 var result = x + y;
 Console.Out.WriteLine("result = {0}", result);
};
action();

 

当你在代码调试器(debugger)里观察“action”时,会发现很有趣的事情。我们可以看到,C# 编译器为我们创建了一个 Target 类,里面封装了 x 变量:

技术分享图片

 

 

 

 

 如最上面的栗子中,我们在fn0(for中定义第一个的result),以外的地方调用它是,变量依然是存在的;这个就是我们闭包生气的地方;

 何避免闭包陷阱呢?C#中普遍的做法是,将匿名函数引用的变量用一个临时变量保存下来,然后在匿名函数中使用临时变量

 

我们再来看一段经典的js代码:

function show(){
  
    var n=12;
    return function (){
      n++;
          console.log(n);
    }

}

var fuck=show();
fuck();
fuck();
fuck();

结果:

 技术分享图片

key point:循环嵌套引用;

同样的代码c#如何实现呢;

 

    public class Person
    {
        public Action show()
        {
            var n = 12;
            return () =>
            {
                n++;
                Console.WriteLine(n);
               
            };
        }
    }

  public class Program{
static void Mian(string [] args){ Person p
= new Person(); var fuck = p.show(); fuck(); fuck(); fuck();
}
}

 结果:

技术分享图片

 从上面的代码我们不难看到,变量n实际上是属于函数T1的局部变量,它本来生命周期应该是伴随着函数T1的调用结束而被释放掉的,但这里我们却在返回的委托b中仍然能调用它

 


评论


亲,登录后才可以留言!