函数的不同形态
函数可以是个表达式
在上文《JavaScript函数》我们介绍了JavaScript
函数是一种语法结构,它可以封装一个代码块,用于重复使用代码。
如下:
function add(a,b){
return a + b;
}
以上代码以函数定义的方式声明了一个用于加和的函数,我们可以通过函数名add
重复使用这段代码。
实际上,我们还有另外一种定义函数的方法,就是把函数当作一个表达式,或者说是函数表达式,还可以把这个表达式赋值给一个变量。
如下:
let add = function(a,b){
return a+b;
};//这里有分号
//以下这种方式可能更容易理解
let add = function(a,b){
return a+b; };
上述代码是一个赋值语句,左侧是一个使用let
关键字定义的add
变量,右侧就是一个函数表达式,语句的意思就是把右侧的函数表达式赋值给左侧的变量add
。
Attention:注意右侧的函数表达式中没有函数名称,在函数表达式中允许省略函数的名称,我们称这种没有名字的函数定义为匿名函数。
后面这种表达式类型的函数定义方式在功能上和前面的函数声明功能是一样的,简单来讲,就是定义一个函数,并把它存放到add
变量中。
在使用函数表达式的时候,语句末尾需要加上
;
,这个分号是赋值语句的一部分,而不是函数定义的语法要求。
函数的本质是一个值
以上代码体现了两种不同的定义函数的方法,但不论使用哪种方法,都不能改变函数是一个值的本质。
以上两种定义方法,都使用了名为add
的变量存储了一个函数,我们还可以打印变量的值:
function add1(a,b){
return a+b;
}
let add2 = function(a,b){
return a+b; };
console.log(add1);
console.log(add2);
打印结果如下:
这就证明了在JavaScript
中,函数是一个特殊的值,如果我们打印这个值,就会得到函数的源码字符串。
需要注意的是,不论使用哪种方式定义函数之后,如果直接使用函数名,例如
add1
、add2
函数并不会执行,调用函数需要加上()
。
既然函数是一个值,我们就可以对其进行一些变量的操作,例如赋值,复制等。
举个小李子:
function add(a,b){
//定义了一个函数变量add
return a+b;
}
let otherAdd = add; //变量复制
console.log(add(1,1));
console.log(otherAdd(1,2));
代码执行结果:
以上代码发生了什么:
- 首先使用函数声明,创建一个函数,并把函数放到变量
add
中; - 将函数变量
add
中的值(也就是函数)复制给另一个变量otherAdd
,注意这里的add
后面没有()
,如果带上()
就是把add()
的函数执行结果赋值给otherAdd
变量了; - 最后
add
和otherAdd
内部存储了同样的函数,在以函数方式调用时,会产生同样的作用,即实现数字加和。
当然,代码写成下面的方式是同样的效果:
let add =function(a,b){
return a+b; };
let otherAdd = add;
console.log(add(1,1));
console.log(otherAdd(1,2));
一个字符串或者数字代表了“数据”的值,一个函数可以理解为“行为”的值;
它们相同的地方在于都可以作为变量的值进行传递;
不同的地方在于“数据”值用于计算、比较,“行为”值可用于“执行”。
回调函数
上文讲到了函数的本质是一个特殊的值,既然是个值,那么就可以作为参数传递!
作为参数传递的函数,通常被称为回调函数。
举个栗子:
function ask(ques, yes,no){
if(confirm(ques)){
yes();
}else{
no();
}
}
function selectedYes(){
console.log('安排');
}
function selectedNo(){
console.log('下次一定');
}
// 将selectedYes和selectedNo作为参数传入ask函数
ask('点个关注,干不干?',selectedYes,selectedNo);
以上代码在实际开发过程中是非常常见的,ask
函数中传入的selectedYes
和selectedNo
两个函数被称为回调函数(回调)。
简单来讲,就是我们以参数的形式传递一个函数,并期望这个函数在未来的某个时刻执行(“回调”)。
例如,在上例中selectedYes
就是回答Yes
的回调,selectedNo
就是回答No
的回调。
除了上例的写法之外,我们还可以直接以匿名函数的形式传递回调函数,从而大幅简写代码:
function ask(ques,yes,no){
if(confirm(ques)){
yes();
}else{
no();
}
}
ask(
'点个关注?',
function(){
console.log('好说');},
function(){
console.log('想得美');}
);//调用
以上这种直接在函数调用过程中,传入匿名函数的方式是JavaScript
非常明显的特征,也是非常重要的特性。我们会在各种项目中看到这种无时无刻不出现的特征~~~
匿名函数传入回调的方式,可以让回调在
JavaScript
中几乎完全匿名,只有在ask
函数内部才能访问,这正是我们想要的结果~~
函数表达式和函数声明的差别
- 语法差别
函数声明在代码文件中单独创建一个函数:
function add(a,b){
return a+b;
}
函数表达式在一个表达式中或者另一个语法结构中创建函数:
let add = function(a,b){
return a+b;
}
- 引擎在什么时候创建函数
函数表达式是在代码执行到表达式后被创建,从创建只会可以使用
当代码执行到let func = function(){...}
的右侧时,开始创建函数,当函数被赋值给变量func
后可用。
函数声明在所有代码执行之前就会被定义,同时可以使用
这是因为,JavaScript
引擎在执行脚本的时候,会先从整个文件中找到所有的全局函数,然后创建这些函数。
这种特性的结果就是,我们可以在函数声明之前使用函数:
add(1,2); //函数还没有创建的时候就可以使用了
function add(a,b){
//在程序执行之初就被创建
return a+b;
}
代码执行结果如下图:
如果把函数声明成表达式就会报错:
add(1,2);//报错
let add = function(a,b){
return a+b; }
- 作用域
严格模式下,函数声明只在它声明的代码块({...}
)内部可见,代码外无法使用,就像局部变量一样。
举个例子:
let score = prompt('请输入你的期末成绩',0);
//在if语句中声明一个函数
if(score > 80){
function doSomething(){
console.log('好学生');//输出好好学生
}
}else{
function doSomething(){
console.log('找个厂吧');//建议打工
}
}
doSomething();//报错,函数不存在
以上代码理论上当score > 80
时,会定义一个函数,输出"好学生",在其他情况下输出"找个厂吧",然后在if
后面调用。
但是,由于函数声明的特性,它只能在if
语句的代码块内部可见,外面是不能使用的,所以代码会报错。
如何处理呢?这里就可以用到函数表达式,也是二者的区别:
let score = prompt('请输入你的期末成绩',0);
let doSomething;
if(score>80){
doSomething = function(){
console.log('好学生');
}
}else{
doSomething = function(){
console.log('找个厂吧');
}
}
doSomething();
课后作业
使用三目运算符,重写以下代码:
let score = prompt('请输入你的期末成绩',0);
let doSomething;
if(score>80){
doSomething = function(){
console.log('好学生');
}
}else{
doSomething = function(){
console.log('找个厂吧');
}
}
doSomething();