1.定义函数
1.1 具名函数
function 函数名(形式参数1,形式参数2){
语句:
return 返回值;
}
1.2 匿名函数
let a = function(x,y){
return x+y;
}
或者给上面的函数一个名字:
let a = function fn(x,y){
return x+y;
}
上面代码会将等号右边的函数的地址,赋值给变量a。注意:这种方式声明的函数,作用域仅限在当前语句中,在外面是访问不到fn()
的:
1.3 箭头函数
let f1 = x=>x*x
let f2 = (x,y)=>x+y //圆括号不能省略
let f3 = (x,y)=>{console.log('hi');return x+y} //花括号不能省
let f4 = (x,y)=>({name:x,age:y}) //直接返回对象会出错,JS会将花括号括起来的部分当作代码块,所以需要加圆括号
1.4 用构造函数
let fun = new Function('x','y','return x+y')
2.函数的要素
2.1 调用时机
举个鲜明的例子:
可以看到,明明是先调用函数,但是由于打印日志的时机在a改变值之后,结果也不同。
2.2 作用域
全局变量:在顶级作用域声明的变量是全局变量,window的属性是全局变量,其他的都是局部变量。
局部变量:局部变量只能在其作用域中生效。注意:函数可以嵌套,作用域也可以嵌套。举例:
function f1(){
let a=1;
function f2(){
let a = 2;
console.log(a);
}
console.log(a);
a=3;
f2();
}
f1()
结果为:1 2
,a=3
没有影响f2函数中a的值。
作用域规则: 如果多个作用域有相同的变量a,那么查找a的声明时,就向上去最近的作用域,简称【就近原则】。像上面的例子中,a的作用域与函数执行无关,那么这种作用域称为【静态作用域】。
2.3 闭包
把上面的例子改一改:
function f1(){
let a=1;
function f2(){
let a = 2;
function f3(){
console.log(a);
}
a=22;
f3();
}
console.log(a);
a=3;
f2();
}
可以看到f3
中用到了外部变量,所以可以引出闭包的定义:如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包。
2.4 形式参数
形式参数的意思就是非实际参数。
function add(x,y){
return x+y;//x,y就是形式参数,而不是实际的参数。
//函数调用时传入的实际数值,才是实际参数
}
形参没有必要完全声明完,例如:
或者多声明形参,例如:
在Java中就不能这样调用。
2.5 返回值
每个函数都有返回值,就算没写return
,返回的值也是undefined
。
2.6 调用栈
什么是调用栈:JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组中。这个数组就叫做【调用栈】,这个栈的深度不是无限长的,当深度达到上限时,会【爆栈】。举例:
这个栈的深度上限,可用:
function computeMaxCallStackSize(){
try {
return 1+computeMaxCallStackSize();
} catch(e){
return 1;
}
}
测试不同浏览器的栈深度上限。
Chorme为12578,Firefox为26773
2.7 函数提升
【函数提升】:不管把具名函数声明在哪里,他都会跑到第一行。举例:
使用赋值形式声明的函数,则不会提升:
2.8 arguments(除了箭头函数)
在函数中,可以用arguments
获取函数的所有参数。举例:
可以看到,arguments是一个数组,但是是一个伪数组(没有push,pop)方法。
2.9 this(除了箭头函数)
在JS的函数中,this默认指向window:
我们可以通过call修改this的指向:
但是,JS会默认帮我们把1封装为对象,这时候就需要将函数声明代码修改为:
function fn(){
'use strict'
console.log(this)
}
这时候,我们就可以得到原始的对象:
call
方法中,还可指定方法的参数:
上面的call方法,还可以用
fn.apply(1,[2,3,4])
替代。
举一个更加清楚的例子,介绍使用call调用函数:
3.立即执行函数
当我们需要一个方法,需要让他立刻执行,但是又不想定义一个全局的函数对象。则可以通过操作符+匿名函数
的形式声明并调用函数。举例:
NaN是函数的返回值,不用理会。当然,还可以使用
! ~ () + -
都可以,推荐用!