这是我参与「第四届青训营 」笔记创作活动的第5天
闭包基础
基本定义
维基百科:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定
这么长的一段话,要配上图和解释才好
头等函数:编程语言中,函数可以当作函数的返回值、入参、变量
词法绑定:把相关的符号和变量值绑定在一起
约束变量:函数内部绑定的符号,内部声明的变量值,也可以是函数外部传入的参数(入参)【一定有】
自由变量:在函数外被定义,但在函数内被引用的,被闭包所捕获的【可以没有】
闭包与函数的区别:“当捕捉闭包的时候,它的自由变量会在捕捉时被确定”,虽然自由变量声明在外部,但在闭包声明的时候,会把相关的自由变量捕捉过来,可以脱离捕捉时的上下文被正常使用。
对捕捉的自由变量的处理方式:1.值拷贝;2.名称引用
函数的入口地址:函数的入口地址称为函数指针。在C++中,函数名是函数的入口地址
网上查的资料: 闭包是能够读取其他函数内部变量的函数,在js中,可以将闭包理解成“函数中的函数”。
引用:什么是闭包?(定义、特性)
闭包由两部分构成:函数(外部函数嵌套的内部函数)、创建该函数的环境
函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
function f1(){
var n=999;
function f2(){
alert(n);
}
}
var test = f1();// 外部函数调用之后其变量对象n本应该被销毁
test();// alert 999,但闭包阻止了它们的销毁,所以仍然可以访问外部函数的变量对象
函数f2被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的;但是f2内部的局部变量,对f1就是不可见的(父对象的所有变量,对子对象都是可见的,反之则不成立)
只要把f2作为返回值,就可以在f1外部读取f1的内部变量,f2函数,就是闭包
本质:闭包是将函数内部和函数外部连接起来的桥梁
不同语言中的表现形式
c++ lambda表达式
c++中闭包表现形式就是lambda表达式
先看学习资料里的例子:
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}
作者:青训营官方账号
链接:https://juejin.cn/post/7123446183363608613/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
由于没有c++基础,我就去查了查资料
引用:C++ lambda表达式
lambda 表达式 定义了一个匿名函数,并且可以捕获一定范围内的变量
语法形式:[caoture] (params) opt -> ret {body;};
capture是捕获列表;
params是参数表;
opt是函数选项;
ret是返回值类型;
body是函数体
在学习资料的例子中,是一个绝对值排序的函数,先调用了一个std::sort函数,在最后传入一个闭包。
闭包不捕获任何变量,有两个入参a,b,在{}中实现了判断ab哪个大
闭包的要素,有函数的入口地址,有约束变量(函数的入参),无自由变量
❓是不是说,闭包要有的外部函数嵌套的内部函数就是std::sort
Python
def make_multiplier_of(n):
def multiplier(x):
return x * n;
return multiplier;
Python中的闭包一般以函数内的嵌套函数来表示。
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包。
外层传入参数n,内层用n实现n的函数,再把内层函数返回出去。
闭包的要素,有函数的入口地址,有约束变量(函数的入参),无自由变量
oc
oc中闭包的实现就是block
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
int i = 1024;
void (^printBlock)(void) = ^{
printf("%d\n", i);
};
printBlock();
}
return 0;
}
作者:青训营官方账号
链接:https://juejin.cn/post/7123446183363608613/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
此处捕获了外部传入的值i,并非作为入参传入的变量,是自由变量
block基础
block和函数相似
block是闭包在Objective-C中的实现
block可以接受参数也可以有返回值
block可以分配在栈和堆上,也可以是全局的。分配到栈上的块可以拷贝到堆中,同标准的Objective-C对象一样,具备引用计数
标准格式
苹果提供快捷键在实现文件的方法体中 敲 inl 直接回车,就可以出现一个block的基本块为啥我实现不了
returnType(^blockName)(parameters) = ^returnType(parameters){
statements
}
returnType:返回值
^:在OC中^操作符是用来声明一个block变量,^也标示着一段block文字的开始
blockName:闭包名
parameters:输入参数
示例:
int (^sumBlock)(int a, int b) = ^int(int a, int b) {
return a + b;
};
int sum = sumBlock(1, 1);
常用简写
省略写法
学习文档中提供了“有返回有入参”、“无返回无入参”两种省略写法,网上资料:
1,无参无返回: void (^myBlock)() = ^(){ ...... };(无参的话前面小括号可以省略,后面分号不能少).
2,有参无返回: void (^myBlock)(int,int) = ^(int a, int b){ .......};(有参数的话,'='号后面的形参名不能省).
3,无参有返回: int (^myBlock)() = ^{.......return....};
4,有参有返回:int (^myBlock)(int , int) = ^(int a ,int b){ ....return a+b;};
都省略了=后面的返回值类型
typedef
对于一些常见的block类型,会用typedef定义一些常用的名称
typedef returnType (^blockName)(parameters);
在返回值类型的左边加上typedef,之后就可以直接使用blockName了
示例:
typedef int (^SumBlock)(int a, int b);
SumBlock block = ^(int a, int b) {
return a + b;
};
首先定义了一个SumBlock类(用typedef声明的事类,命名一般与类的命名相同,首字母大写),入参为a,b。定义一个block对象,就可以直接使用了。
❓typedef是一个起别名的声明,所以示例中typedef声明给“返回值是int,入参为a,b”这样的闭包起别名为SumBlock
练习
两数相减的block:
先交先交,剩下的下午看