Javascript闭包和C#匿名函数对比分析
C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响。人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和高效很多,比如回调和事件处理都特别适合使用表达式中直接编写函数的形式,因此C#的匿名函数也就应运而生。
初识C#中的匿名函数,多多少少并不是那么直观,在匿名函数中,可以直接使用该匿名函数所在的函数中的局部变量,这和Javascript闭包函数在语法形式和运行结果上非常相似,但两者在实现原理上却完全不同,后者是语言内在特性,而前者(C#匿名函数)只是一个编译器功能,也称语法糖。
1. Javascript闭包
作者在《Javascript本质第一篇:核心概念》和《Javascript本质第二篇:执行上下文》这两篇文章中对Javascript的核心特性——包括执行上下文——做了详细介绍。很多概念都给出了明确定义,似乎缺少了闭包。
先列出一段代码,明确这里关于父子函数的定义,作为参考:
function funParent() { // 父函数
var v = "parent funtion's variable";
function funChild() { //子函数
return v;
};
return funChild;
}
fun = funParent();
Javascript中闭包的定义:
闭包就是函数,函数就是闭包。
在作用域的角度上,将函数称为闭包。
通常在以下场景中我们更趋向于突出一个函数的闭包的概念:一个函数在其函数体中使用了定义该函数的父函数中的var变量,而且这个函数在父函数之外被使用。
例如在上面的代码的中,我们通常将函数fun叫做闭包,而不去刻意突出函数funParent的闭包的概念。
如将上面的代码改为:
function funParent() { // 父函数
var v = "parent funtion's variable";
function funChild() { //子函数
return v;
};
funChild();
}
funParent();
这个时候的funChild函数和前面的示例代码中的funChild或fun在内在结构和行为上没有任何的区别,只是函数及其所引用的执行上下文被释放的时机的问题。
2. C#的匿名函数
C#中可以通过lambda表达式的形式在函数中定义匿名函数:
(参数) => {代码}
在匿名函数的代码中可以使用定义该匿名函数的函数中的局部变量,这一特性与Javascript中函数中的函数一样。
下面的代码在Init函数中定义两级嵌套的匿名函数:
namespace ConsoleTest1
{
class A
{
public Action Show;
public Action<string> Set;
public Action ShowNested;
public Action<string> SetNested;
public void Init()
{
string str = "你好";
this.Show += () =>
{
Console.WriteLine(str + "!");
string name = "张三";
ShowNested = () =>
{
Console.WriteLine(str + "," + name + "!");
};
SetNested = (v) =>
{
name = v;
};
};
this.Set += (v) =>
{
str = v;
};
}
}; class Program
{
static void Main(string[] args)
{
var a = new A();
a.Init(); a.Show();
a.Set("Hello");
a.Show(); a.ShowNested();
a.SetNested("Zhang San");
a.ShowNested();
}
}
}
运行结果如下:
你好!
Hello!
Hello,张三!
Hello,Zhang San!
请按任意键继续. . .
匿名函数的运行行为与Javascript中闭包函数的运行行为相似。
但是,在C#中,name和str明显不符合局部变量的行为特性,通过反编译生成的exe文件,可以看到,Init函数已经被编译器完全重构,专门的类被创建,来封装name和str变量,实现匿名函数。匿名函数最终还是由有名函数实现。
反编译结果如下:
namespace ConsoleTest1
{
internal class A
{
[CompilerGenerated]
private sealed class <>c__DisplayClass4
{
private sealed class <>c__DisplayClass6
{
public A.<>c__DisplayClass4 CS$<>8__locals5;
public string name;
public void <Init>b__1()
{
Console.WriteLine(this.CS$<>8__locals5.str + "," + this.name + "!");
}
public void <Init>b__2(string v)
{
this.name = v;
}
}
public string str;
public A <>4__this;
public void <Init>b__0()
{
A.<>c__DisplayClass4.<>c__DisplayClass6 <>c__DisplayClass = new A.<>c__DisplayClass4.<>c__DisplayClass6();
<>c__DisplayClass.CS$<>8__locals5 = this;
Console.WriteLine(this.str + "!");
<>c__DisplayClass.name = "张三";
this.<>4__this.ShowNested = new Action(<>c__DisplayClass.<Init>b__1);
this.<>4__this.SetNested = new Action<string>(<>c__DisplayClass.<Init>b__2);
}
public void <Init>b__3(string v)
{
this.str = v;
}
}
public Action Show;
public Action<string> Set;
public Action ShowNested;
public Action<string> SetNested;
public void Init()
{
A.<>c__DisplayClass4 <>c__DisplayClass = new A.<>c__DisplayClass4();
<>c__DisplayClass.<>4__this = this;
<>c__DisplayClass.str = "你好";
this.Show = (Action)Delegate.Combine(this.Show, new Action(<>c__DisplayClass.<Init>b__0));
this.Set = (Action<string>)Delegate.Combine(this.Set, new Action<string>(<>c__DisplayClass.<Init>b__3));
}
}
}
反编译出来的类A的定义与源代码中类A的定义已经不同,通过编译器的重构,以基本的C#语法实现了匿名函数和类似Javascript中闭包的功能。
3.结论
Javascript中所有函数本质上都是闭包,是在作用域的角度上对函数的称谓。
C#中的匿名函数行为特性上类似Javascript中闭包,通过编译器重构实现。
在Javascript中,函数是一个对象,因此函数中定义函数就是一件非常正常的事情。如果:
function foo() {
var bar = new Function("val", "return val");
return bar("test");
};
foo();
看起来很正常,那么:
function foo() {
function bar(val) {
return val;
};
return bar("test");
};
foo();
也是很正常的。
Javascript闭包和C#匿名函数对比分析的更多相关文章
- 理解javascript的闭包,原型,和匿名函数及IIFE
理解javascript的闭包,原型,和匿名函数(自己总结) 一 .>关于闭包 理解闭包 需要的知识1.变量的作用域 例1: var n =99; //建立函数外的全局变量 function r ...
- Javascript 闭包与高阶函数 ( 二 )
在上一篇 Javascript 闭包与高阶函数 ( 一 )中介绍了两个闭包的作用. 两位大佬留言指点,下来我会再研究闭包的实现原理和Javascript 函数式编程 . 今天接到头条 HR 的邮件,真 ...
- 闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别
闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别 函数最常见的形式是具名函数(named function): function foo(){ con ...
- js循环函数中的匿名函数和闭包问题(匿名函数要用循环中变量的问题)
js循环函数中的匿名函数和闭包问题(匿名函数要用循环中变量的问题) 一.总结 需要好好看下面代码 本质是因为匿名函数用到了循环中的变量,而普通方式访问的话,匿名函数的访问在循环之后,所以得到的i是循环 ...
- JavaScript基础---作用域,匿名函数和闭包
匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数. 一.匿名函数 //普通函数 function box() { //函数名是 box return 'TT'; } //匿名函数 f ...
- JavaScript基础---作用域,匿名函数和闭包【转】
匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数. 一.匿名函数 //普通函数 function box() { //函数名是 box return 'TT'; } //匿名函数 f ...
- 【转】javascript变量作用域、匿名函数及闭包
下面这段话为摘抄,看到网上大多数人使用的是变量在使用的时候声明而不是在顶端声明,也可能考虑到js查找变量影响性能的问题,哪里用就在哪里声明,也很好. 在Javascript中,我们在写函数的时候往往需 ...
- Javascript 闭包与高阶函数 ( 一 )
上个月,淡丶无欲 让我写一期关于 闭包 的随笔,其实惭愧,我对闭包也是略知一二 ,不能给出一个很好的解释,担心自己讲不出个所以然来. 所以带着学习的目的来写一写,如有错误,忘不吝赐教 . 为什么要有闭 ...
- javascript闭包和立即执行函数的作用
一.闭包——closure 先看一个闭包的例子.我们想实现一个计数器,最简单的方法就是定义一个全局变量,计数的时候将其加1.但是全局变量有风险,哪里都有可能不小心改掉它.那局部变量呢, 它只在函数内部 ...
随机推荐
- ubuntu_tftp服务搭建
搭建过程: 1. sudo apt-get install tftpd-hpa tftp-hpa是客户端 tftpd-hpa是服务器端 2.建立目录 执行:mkdir /home/wmx/Deskto ...
- SqlLite 基本操作
1.数据类型 ● SQLite将数据划分为以下⼏几种存储类型: ● integer : 整型值 ● real : 浮点值 ● text : ⽂文本字符串 ● blob : ⼆二进制数据(⽐比 ...
- QT生成流水账号
在做数据库课程的时候,要生成财务表,每条记录应该有一个流水账号. 实现思路: 当前时间+随机一个四位数 上代码 //生成流水号 QString adminRecharge::getNumber() { ...
- webpack2新特性
增加 import() 作为代码分割点:System.import已被弃用,在webpack3时会被完全移除: 内置了json加载器,不再需要单独配置了 当打包文件过大时会提示性能警告,可以用 per ...
- oracle(sql)基础篇系列(四)——数字字典、索引、序列、三范式
数字字典表 --查看当前用户下面有哪些张表 select * from user_tables; select table_name from user_tables; --查看当前用户下面有 ...
- linux內核輸出soft lockup
創建的內核線程長期佔用cpu,一直內核認為線程soft lockup,如無法獲取自旋鎖等:因此線程可適度調用schdule(),以進行進程的調度:因為kwatchdog的執行級別低,一直得不到執行 [ ...
- css自定义三角形效果
废话不说了,直接上代码 element{ width:0px; height:0px; border-left:10px; border-right:10px; border-bottom:10px; ...
- sublime插件
CnDict: 中英文字典软件,快捷键查词,目前支持金山词霸和有道词典. BracketHighlighter: 有个笑话,说前苏联间谍花了巨大的代价,偷到了阿波罗飞船的最后一屏的代码,发现全部是 } ...
- 0421 & SX2016
山西省选...这个省...不算强吧...然而就是这么腊鸡题目还是wa得一无是处...怎么办啊怎么办啊...无处拯救青春和未来啊... T1: POI2004原题 BZOJ1524 n<=16.这 ...
- LINUX常见问题
FQA1:如何进入linux单用户模式修改root密码 进入单用户模式:1. grub进入启动画面之后,敲入“e”,把光标移动到kernel ...那一行,再敲入“e”,在kernel 一行的最后加上 ...