- 设置函数默认值
- 结合解构赋值默认值使用
- 利用rest(…变量名)传入任意参数
…rest和扩展运算符
rest参数(…变量名),用于获取函数的多余参数,rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。
1
2
3
4function foo(...y){
console.log(y) // [1,2,3,4]
}
foo(1,2,3,4)
利用扩展运算符(…数组)替代数组的apply方法
在es5中,需要用apply将数组转化为函数参数,在es6中就不需要这种方式了,可以使用…来代替
1
2
3
4
5
6function f(x, y, z) {
console.log(x + ' ' + y + ' ' + z);
}
var args = [1, 2, 3];
// f.apply(null, args)
f(...args)
数组push的例子:
1
2
3
4
5
6
7var arr1 = [0, 1, 2]
var arr2 = [3, 4, 5]
var arr3 = arr1.concat(arr2)
console.log(arr3) //[ 0, 1, 2, 3, 4, 5 ]
var arr4 = arr1.push(arr2)
console.log(arr1) //[ 0, 1, 2, [ 3, 4, 5 ] ]
如果想使用arr1.push方法的话,就需要用apply传arr2了
1
var arr4 = Array.prototype.push.apply(arr1, arr2) //[ 0, 1, 2, 3, 4, 5 ]
如果使用扩展运算符的话,就简单一些了
1
var arr4 = arr1.push(...arr2)
name属性
函数的name属性可以返回函数名
1
2
3
4function abc(){
console.log(abc.name)
}
abc() // 'abc'
函数的静态变量和函数
1 | function Box(){} |
静态变量和静态函数是Box对象的属性和方法,不属于实例。
函数的实例函数和变量
1 | function Box(){ |
每个实例都有一套实例属性和实例方法,互不影响。
原型上的属性和方法,是实例共用的。
函数传参
基本类型 (基本类型的变量复制)
1 | var count = 10; |
引用类型
1 | var person = { |
1 | var person = { |
person传递给函数中的peo,但在函数内部peo又指向了一个新对象,所以result.name是新对象的值,person还是指向原对象,所以并没有改变。
ECMAScript中所有函数的参数都是按值传递的。 ——《JS高程》
我们可以把ECMAScript函数的参数想象成局部变量,在向参数传递基本类型的值时,被传递的值被复制给一个局部变量。
在向函数传递引用类型时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。
即使在函数内部修改了参数的值,但原始的引用仍然保持未变。
尾调用优化
介绍
尾调用指某个函数的最后一步是调用另一个函数。
demo
1 | function f(x) { |
为什么尾调用
函数调用会在内存形成一个“调用记录”,又称“调用帧”。每形成一个调用帧就会占用一定的内存,假如有一个函数A里面调用了函数B,函数B里面又调用了函数C,以此类推。
在不是尾调用的情况下,因为js是单线程同步的,所以只有当函数B执行完毕才会执行函数A的return语句(函数没写return会有一个默认的return undefiend;),此时函数B的调用帧才会消失。但事实上,只有当函数B执行到return语句时才会执行完毕,而在函数B执行return之前会先调用函数C,所以当这种函数调用越来越多的时候,形成的调用帧也会越来越多占用的内存就会越来越大。俗话说精满自溢,内存也会有溢出的时候是吧,这就不行了!所以要用尾调用!
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。
比如在执行递归的时候就可以利用尾调用达到优化内存的目的。函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
1 | // 递归 计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) |
函数声明覆盖
1 | function foo(){ |