<1> 函数本质是对象
函数本质上是一个对象,因为函数都是Function的实例,所以每个函数都有自己的属性和方法,且函数名里保存的就是该函数对象的地址!!!
<2> 函数定义的四种形式
1) 函数声明
function sum(){}
2) 函数表达式
let sum = function(){}
3) 箭头函数
let sum = ()=>{}
4) 使用 Function 构造函数 new 出来【不推荐】
let sum = new Function(参数,函数体)
<3> 函数参数
ES 中规定的函数既不关心传入的参数的个数,也不关心传入参数的类型!!【比如定义时给函数设置了两个参数,那我们调用时,就可以只传入一个,或者三个,或者不传,即可以传入任意多个!!】
1) 参数的扩展与收集 ...
- 扩展 ...
let val = [ 1 , 2 , 3 ]
// 函数调用
fn(2,...val)
- 收集 ...
function fn(num1,...values){
console.log(values)
}
// 函数调用
fn(1,3,4,5,6) //[3,4,5,6]
注意: 参数搜集只能放在所有参数的后面!!!!
2) 没有函数重载
因为 ES 中的函数不关心传入参数的个数和类型,所以说 ES 中的函数就没有函数签名(接收参数的数量与类型)!!!所以就没有函数重载!!
函数重载:
- 一个函数可以有多个定义,只要接收参数的个数和类型不同就是,会根据调用时传入参数的情况来决定调用哪个!!!!
所以:
- ES 中的函数,如果定义了同名函数,那么后定义的会覆盖前面定义的!!
<4> 函数提升
- 函数声明有函数提升
- 函数表达式没有函数提升
对于
函数声明, JS 引擎会在执行任何代码之前,先读取函数声明,并在执行上下文中生成函数定义。
对于
函数表达式,必须要等到代码执行到那一行,才会在执行上下文中生成函数定义!!!
<5> 函数内部的 new.target
ES6 中新增了检测函数是否是通过 new 调用的 new.target 属性!!
new.target,在类或者函数中使用,指向用 new 调用的类或者函数!!
- 如果是普通调用,new.target 的值为 undefined
- 如果是使用 new 关键字调用的,new.target 的值为被当作构造函数调用的函数
<6> 函数的共有属性
每个函数都有三个属性,length 、caller 、prototype
- length 的值为函数定义时,命名参数的个数
- caller 的值为调用当前函数的函数(即该函数外部包括的函数),如果是在全局作用域下调用的,那么 caller 的值为 null
<7> ES6 严格模式下的尾调用优化
【尾调用是一项内存管理优化机制!!!】让 JS 引擎在满足条件的情况下可以实现重用栈帧!!!
before,有几个嵌套函数就有几个栈帧,现在只有一个。
need满足的条件:
- 在严格模式下
- 外部函数的返回值是对未个函数的调用
- 尾调用函数执行后不需要执行额外的逻辑
- 尾调用函数不是引用外部函数作用域中自由变量的闭包!!
栗子:
function outter(){
return inner()
}
- 按照之前: 当 outter 函数执行到最后一步 return inner() ,要先调用 inner() ,所以因为此时 outter() 函数还没有执行完毕,所以还会保留其
执行上下文栈帧!!然后执行 inner() 时,又会为 inner() 创建一个新的上下文栈帧,并压入栈!!! - 运用上尾调用后:当 outter 函数执行到最后一步 return inner() ,因为后续没有其他要执行的代码,所以留着 outter() 的执行上下文也没多大意义,就可以把它弹出,再为 inner() 创建上下文!!!
这对递归调用的优化十分大,因为递归调用会在短时间内创建大量的函数执行上下文(栈帧),并压入栈!!!