大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: 编程语言 修改

首先声明:“面向函数”是飞哥“自定义”的一个说法,算是一种借用,和面向过程/面向对象并列,便于记忆和理解,但并不严谨。

类似的、主流的说法是函数式编程,但和我想表达的内容还是有一些差距。


作为变量的函数

一般我们认为变量是一种数据,函数是一种操作。

数据是可以传递的,比如变量赋值、函数传参之类的。

但操作呢?操作/动作/行为能不能传递?……

随着编程语言的发展(当然根源是开发的需要),现代编程语言普遍允许将函数也视为(或类似于)一种值或变量,可以传递。

随之产生了各种各样的语法现象:

  • 回调(callback):JavaScript
  • 委托(delegate):C#
  • Lamda表达式:C#和Java
为什么需要?不搞明白这个问题,你越学越晕!


重用

需求:找出数组中满足某种条件的数。

某种条件,可以是大于0,于是你的代码就是:

	for (let i = 0; i < numbers.length; i++) {
		if(numbers[i]>0){ //就筛选条件变化着的
			console.log(numbers[i]);		
		}//else nothing
	}

也可能是奇数,那么你的代码就是:

	for (let i = 0; i < numbers.length; i++) {
		if(numbers[i]%2==1){ //就筛选条件变化着的
			console.log(numbers[i]);		
		}//else nothing
	}

注意:这两段代码我们进行了复制粘贴:

(格言:程序员厌恶复制粘贴。)

然后只改了一点点……

以后还可能是其他任何稀奇古怪的要求,比如能同时被3和5整除……

我们能不能把这些代码重用一下?

经过观察,我们发现,上述代码中唯一不同的就是筛选条件,假如我们把两段代码封装成一个方法,把筛选条件作为一个变量/参数……

PS:这就有点“封装变化”的味道了


函数变量和匿名函数

JavaScript变量就可以直接被函数赋值:

	let where = function(number){
		return number > 0;
	}
等号右边就是一个函数,只是没有名字,所以被称之为匿名函数

被函数赋值之后,where就变成了一个函数变量,实际上也就是一个函数。演示:

  • where(23)调用
  • 控制台输入where查看

既然where是变量,那么就可以被重新赋值:

	where = function(number){
		return number % 2 == 1;
	}
甚至把命名函数赋值给where也是没有问题的:(注意:是alert不是alert(),没有圆括号)

对的,JavaScript就这么任性(但强烈建议!)


回调:把函数当做参数传递

匿名函数就是“值”(同32,"源栈",[12, 32, 18, 96])一样的存在,可以被赋值给变量,也可以被直接用作参数。

综上,就可以封装这么一个filter函数:

	function filter(numbers, where){    //参数where是一个回调函数
		for (let i = 0; i < numbers.length; i++) {
			if(where(numbers[i])){    //where被作为函数直接使用
				console.log(numbers[i]);		
			}//else nothing
		}
	}

把函数作为参数,传递给另外一个函数,作为参数的函数就被称之为回调函数

调用filter时,既可以传递一个(命名)函数名,也可以是函数变量,

filter([1, 8, -13, 21, -9], where)    //注意where后面没有圆括号
甚至还可以是匿名函数:
filter([1, 8, -13, 21, -9], function(number){
	return number<0;}
)


Lambda表达式

  • JavaScript是弱类型语言,变量想装啥就装啥;
  • 但C#是强类型语言,一个函数算啥类型啊?所以推出了delegate;
  • Java一直揣着,不屑的翻了个白眼:旁门左道!要面向对象,要面向对象,要面向对象,知道吗?

……时光飞逝……

  • C#进化:匿名方法 => 划时代的Lamda,好评如潮
  • Java:骂骂咧咧的推出了Lambda
  • JavaScript:ES6中推出了类Lambda的“箭头函数”

本质上,Lambda就是一个匿名函数。而且省略掉了function/delegate关键字,引入了一个箭头

  • =>(C#和JavaScript),或
  • ->(Java)

以JavaScript为例:

 let where = (number) => {
		return number > 0;
	}

然后,再引入一些简写规则:

let where = number => {  //一个参数可以不加()
let where = () => {  //但没有参数也要保留圆括号()
let where = () => alert('一行代码省略花括号');
let where = (number) => number > 0;  //连return也省略了


闭包

根据变量的作用域规则,lambda表达式中可以使用其外部的变量:

let number = 10;
let where = () => {
		return number > 0;
	}

这就是(广义上的)闭包。

如果:这个使用了外部变量lambda表达式,是作为一个方法的返回值:

function find() { 
	let number = 10;
	return () => {
			return number > 0;
	}
}
这就会形成(狭义上的)闭包,从而产生一个结果:

lambda表达式延长了number的生命周期。

  • 如果find()中没有lambda表达式,或者lamda表达式中没有使用numbernumber应该在find()被调用结束后被销毁(出栈)
  • find()执行完成返回的是一个(尚未执行的)函数,该函数直到再次被调用才开始真正执行
  • 所以number应该被保留,不能被销毁

断点演示:

为什么是#常见面试题#:在早期的JavaScript中,使用var定义变量,会带来一些“莫名其妙”的问题……



事件机制

重用,只是“函数做变量”的一个作用。事件机制,才是它的必然。

为什么叫做回调?

正常调用:我要实现某个功能,需要某个函数(我知道这个函数干嘛的),于是我开开心心的去调用它。

但回调:

  • 我(filter)要调用某个函数(where(numbers[i])),但其实我不知道它(where)究竟要干嘛呢……懵+1
  • 嗯,当我(filter)被某个小可爱调用的时候,他会告诉我这个函数要干嘛的(filter(numbers, x=>x>0))……懵+2

为什么要搞得这么复杂?

事件

编程开发的事件里,比如点击一个按钮,就可以触发了一个事件(event)。类似的还有:移动鼠标、右键、按下键盘……都可以触发事件。

按钮是谁做的?以后你们就会知道:按钮是浏览器厂商做好的,我们只需要简单的声明就可以生成:

<button>我是按钮</button>
但是,浏览器厂商的开发人员在生成这个按钮的时候(一样要写代码),
  • 要预留一个功能:能够被点击(click),并且点击之后能够予以响应;
  • 但是并不知道如何响应(是弹出一个中奖窗口呢,还是地球即将毁灭的警告),如何响应是我们Web开发人员去设置的

怎么办?在线等,挺急的……

就让button暴露一个click事件(算了,我通俗点……)。大致对话内容如下:

  • 浏览器开发:你声明button的时候可以指定(绑定)click事件哟!绑定之后,用户只要一点击,我就会执行你的指示
  • Web开发:好勒!但是,我怎么“指示”你呢?

当然是函数了:

function welcome(){
    alert("一起帮·源栈课堂欢迎您!")
}

这个函数就是回调函数,因为它会被作为参数传递给button:

<button onclick="welcome()">我是按钮</button>
在button被点击时调用(注意:不是一声明就调用)


作业

  1. 声明一个匿名函数,能够求两个参数的和,然后将其赋值给变量calculate
  2. 给calculate用箭头函数重新赋值,让其能够求两个参数的差
  3. 【难:参考声明一个命名函数merge,能够接受两个参数:一个数组numbers,一个回调函数calculate;
    然后merge()中调用calculate,将数组中相邻两个数进行运算(求和求差等),返回一个新数组。
    比如:传入numbers是[2,9,18,20,15,6],calculate是取和,则merge()函数返回数组为:[11,38,21]
    说明:传入的数组元素个数始终是偶数,不考虑元素奇数个数情景
  4. 【难:参考】将merge函数绑定到button的click事件上,点击button运行
回调 委托 Lamda
觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 编程语言 中继续学习:

我们的 特色

  • 先学习,后付费
  • 线上/线下,自由组合

更多了解

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧