函数参数的默认值
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面,这种写法的好处
- 相对简洁
- 方便阅读代码的人可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档
- 有利于将来的代码优化,即使未来版本彻底拿掉这个参数,也不会导致以前的代码无法运行
参数变量是默认声明的,所以不能在函数体内用 let 或 const 再次声明
参数默认值不是传值的,而是每次都重新计算默认值表达式的值,也就是说,参数默认值是惰性求值的
通常情况下,定义了默认值的参数应该是函数的尾参数,这样比较容易看出参数到底省略了哪些参数。如果非尾部的参数设置默认值,实际使用时,这个参数是无法省略的
指定默认值之后,函数的「length」属性将返回没有指定默认值的参数个数,也就是说,指定了默认值之后,length 属性将失真。如果设置了默认值的参数不是尾参数,那么 length 属性也不再计入后面的参数
rest 参数
ES6 引入了 rest 参数(形式为 ...变量名),用于获取函数的多余参数,这样就不再需要使用 arguments 对象了
rest 参数搭配的变量是一个数组,该变量将多余的参数放入其中。rest 参数之后不能再有其它参数,否则会报错。
函数的「length」属性不包括 rest 参数
严格模式
ES5 的函数内部是可以设定为严格模式的
ES6 做了修改,规定只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错
原因:函数内部的严格模式同时适用于函数体和函数参数。但是函数执行时,先执行函数参数,再执行函数体。这样就有一个不合理的地方:只有从函数体之中才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行
name 属性
函数的 name 属性返回该函数的函数名
将匿名函数赋值给一个变量,ES5 的 name 属性会返回空字符串,ES6 的 name 属性会返回实际的函数名
将具名函数复制给一个变量,ES5 和 ES6 的 name 属性都返回这个具名函数原本的名字
Function 构造函数返回的函数实例,name 属性的值为 anonymous
bind 返回的函数,name 属性返回的值会加上 bound 前缀
function foo() {}
foo.bind({}).name // 'bound foo'
(function() {}).bind({}).name // 'bound '
箭头函数
ES6 允许使用箭头(=>)定义函数
箭头函数的注意事项
- 函数体内的「this」对象就是定义时所在的对象,而不是使用时所在的对象
- 不可以当做构造函数,也就是说,不能使用 new 命令,否则会抛出错误
- 不可以使用
arguments对象,该对象在函数体内不存在,如果要用,可以用rest参数代替 - 不可以使用
yield命令,因此箭头函数不能用作Generator函数
「this」对象的指向是可变的,但是在箭头函数中它是固定的。箭头函数根本没有自己的「this」,导致内部的「this」就是外层代码块的「this」
也正是因为它没有「this」,所以不能用作构造函数
除了「this」,「arguments」、「super」和「new.target」在箭头函数中也是不存在,它们分别指向外层函数的对应变量
箭头函数没有自己的「this」,当然也就不能使用call()、apply()、bind() 这些方法去改变「this」的指向
绑定 this
函数绑定运算符是并排的双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象作为上下文环境绑定到右边的函数上
foo::bar
// 等同于
bar.bind(foo)
双冒号运算符返回的还是原对象,因此可以采用链式写法
尾调用优化
尾调用:指某个函数的最后一步是调用另一个函数
function f(x) {
return g(x)
}
函数 f 最后一步调用函数 g,这就是尾调用
尾调用不一定出现在函数尾部,只要是最后一步操作即可
函数调用会在内存中形成一个调用记录,又称调用帧,保存调用位置和内部变量等信息
如果函数 A 的内部调用函数 B,那么会在 A 的调用帧上方形成一个 B 的调用帧,等 B 运行结束,将结果返回给 A,B 的调用帧才会消失。如果函数 B 内部还调用函数 C,那就还有一个 C 的调用帧,以此类推,所有的调用帧就形成一个调用栈
由于尾调用是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不回再用到了,直接用内层函数的调用帧取代外层函数即可
尾调用优化:即只保留内层函数的调用帧
如果所有函数都是尾调用,那么完全可以做到每次执行时调用帧只有一项,这将大大节省内存,这就是尾调用优化的意义
尾递归
函数调用自身称为递归,如果尾调用自身就称为尾递归
递归非常耗内存,因为需要同时保存成百上千个调用帧,很容易发生栈溢出错误。但尾递归只存在一个调用帧,所以永远不会发生栈溢出错误
ES6 的尾调用优化只在严格模式下开启,正常模式下无效
- 因为,正常模式下函数内部有两个变量,可以跟踪函数的调用栈
- func.arguments,返回调用时的函数参数
- func.caller,返回调用当前函数的那个函数
尾调用优化发生事,函数的调用栈会被改写,因此,上面两个变量都会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效
函数参数的尾逗号
函数定义和调用时,不允许最后一个参数有尾逗号,会报错
ES2017 有一个提案,允许函数的最后一个参数有尾逗号