函数式编程相关概念 - 笔记1
Java 8 引入了 lambda 表达式,以及函数式编程风格。在了解函数式编程过程中,做了些笔记,摘录于本文。
嵌套函数( Nested Function )
1. 嵌套函数,是指在另一个函数里面定义的一个函数。外层的函数,这里简称为外层函数。
2. 函数的嵌套可以是多层嵌套。嵌套函数可以看到其全部的外层函数的非局部变量。在实际程序中,嵌套的层数一般很少。下面是一个三层嵌套的例子, innerOfInner 也可以访问在 outer 函数体重定义的变量 x 。
function outer(){ var x = 1; function inner(){ x += 1; console.log(x); function innerOfInner(){ x += 10; console.log(x); } return innerOfInner; } return inner; }
3. 嵌套函数对非局部变量拥有读和写的权限。
4. 如果一个嵌套函数可以逃离外层函数,例如函数是第一公民,并且内嵌的函数被传到另外一个函数作为参数或者被当做返回值,那么,就会产生一个闭包 ( closure ),通过调用逃离的函数,可以访问函数的原本的环境变量。此时,外层函数的框架 ( frame )会继续存在,直到所有指向此框架的闭包消失。闭包所指向的非局部变量会从栈内存被移到堆内存。
5. Javascript 支持嵌套函数。Java 通过 Lambda 表达式,内部类,匿名类来间接支持内置函数。
例子,add 是外层函数,plus 是嵌套函数。counter 对于 add 来说是局部变量,对于 plus 来说则是非局部变量 ( non-local variable )。在一个外层函数 add 的函数体里面,定义了一个嵌套函数 plus ,然后调用嵌套函数,并返回结果。
function add() { var counter = 0; function plus() {counter += 1;} plus(); return counter; }
非局部变量( non-local variable )
1.非局部变量其实是相对的一个称谓,是指既不是局部又不是全局的变量。这个称谓一般用于内嵌的函数,或者匿名函数的上下文当中。
2. 在编程语言中,实现非局部变量比较困难,这也导致了嵌套函数、匿名函数、高阶函数、头等函数等功能难以实现。
3. 如在上面第 4 点中提到,当嵌套函数被逃离外层函数时,一个闭包就会被产生,并被用于定位非局部变量。当外层函数返回内嵌函数时,外层函数执行完毕,从栈上被弹出,非局部变量也会从栈上消失。但是非局部变量仍然被内存函数引用,所以需要在堆上给他们重新分配内存。这样,非局部变量的生命周期就会长于定义它的外层函数。
非局部变量例子简单例子,参照嵌套函数中 add / plus 的代码以及解释。
闭包 ( Closure )
1. 在操作中,闭包是指一条记录,存储着一个函数和它的环境变量。这个函数既可以是有名函数,也可以是匿名函数。
下面例子中,第一个闭包的函数原来是一个有名函数,而第二个闭包的函数原来是匿名函数。
function f(x) { function g(y) { return x + y; } return g; } function h(x) { return function(y) { return x + y } } var ff = f(3); ff(5); // return 8; var hh = h(3); hh(5); // return 8;
闭包可以直接被使用,而不用赋值给其他变量,此时称其为匿名闭包,如下。
f(3)(5); // return 8
2. 原来的嵌套函数例子中,内嵌的函数在外层函数体内被执行,如果外层函数返回嵌套函数,则产生了一个闭包。见下面例子。
function add() { var counter = 0; function plus() { counter += 1; return counter; } return plus; }
参考资料
Closure (computer programming), wikipedia