ES6 函数的扩展

90 阅读3分钟
  1. ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'world') {
    console.log(x, y)
}
log('Hello') // Hello world
log('Hello', 'China') // Hello China
log('Hello', '') // Hello 

参数变量是默认声明的,所以不能用letconst再次声明。

function foo(x=5) {
    let x = 1 // error
    const x = 1 // error
}

参数默认值不是传值的,而是每次会重新计算默认值表达式的值,即惰性求值。

let x = 99
function foo(p = x + 1) {
    console.log(p)
}
foo() // 100

x = 100
foo() // 101
  1. 参数默认值可以与解构赋值的默认值结合使用,避免函数调用时未提供参数导致变量xy无法生成,从而报错。
function foo({x, y = 5}) {
    console.log(x, y)
}
foo({}) // undefined 5
foo() // TypeError: Cannot read property 'x' of undefine

function foo({x, y = 5} = {}){
    console.log(x, y)
}
foo() // undefined 5
  1. 函数的length属性将返回没有指定默认值的参数个数。即如果指定了默认值,length属性将失真。
(function (a) {}).length // 1
(function (a - 5) {}).length // 0

length属性的含义是:该函数预期传入的参数个数。某个参数指定默认值后,预期传入的参数个数就不包括这个参数了。

(function (...arg) {}).length // 0

如果设置默认值的参数不是尾参,那么length属性也不再计入其后面的参数。

(function (a = 0, b) {}).length // 0
  1. 函数的参数会形成一个单独的作用域
let x = 1
function (y = x) {
    let x = 2
    console.log(y)
}
f() // 1

y = x的作用域里,变量x没有定义,所以会指向外层的变量x(如果全局变量x不存在会报错),函数调用时,函数体内的局部变量x不会影响默认值变量x

  1. ES6引入rest参数(形式为...变量名),rest参数是一个数组,且只能是最后一个参数(否则会报错)。
function push(arr, ...items) {
    items.forEach(function(item) {
        arr.push(item)
        console.log(item)
    })
}
var a = []
push(a, 1, 2, 3)
  1. 箭头函数中大括号会被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getItem = id => ({id: id})

使用注意点:
(1)箭头函数没有自己的this对象,内部的this就是定义时上层作用域中的this
(2)不能当作构造对象。
(3)不能使用argumens对象,因为它在函数体中不存在,可以用rest参数替代。

  1. 尾调用是指某个函数的最后一步是调用另一个函数。
function f(x) {
    return g(x)
}

尾调用不一定出现在函数尾部,只要是最后一步操作即可。

funtion f(x) {
    if (x > 0) {
        return m(x)
    }
    return n(x)
}

上面代码中,函数mn都属于尾调用。
函数调用会在内存中形成一个调用记录,即调用帧,保存了调用位置和内部变量等信息。尾调用优化是指只保存内层函数的调用帧,这可以大大节省内存。

function f() {
    let m = 1
    let n = 2
    return g(m + n)
}
f()

// 等同于
function f() {
    return g(3) 
}
f()

// 等同于
g(3)

但只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧。

function addOne(a) {
    var one = 1
    function inner(b) {
        return b + one
    }
    return inner(a)
}

上述函数不会进行尾调用优化,因为内层函数inner用到了外层函数addOne的内部变量one

  1. 尾调用自身称为尾递归,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
function factorial(n, total) {
    if (n === 1) return total
    return factorial(n - 1 ,n * total)
}
  1. 函数实例的toString()返回函数代码本身。
 function /* foo comment */ foo () {}

foo.toString()
// "function /* foo comment */ foo () {}"
  1. ES2019允许catch语句省略参数。
try {
    // ...
} catch {
    // ...
}