ES6:函数拓展内容

ES6中函数内容的新特性

参数设定默认值

ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x,y='world'){
    
    
	console.log(x,y);
}

log('hello')	//hello world
log('hello','yivi')	//hello yivi
log('hello','') //hello 

参数变量是默认声明的,因此不能再用let或const再次声明,也不能用同名的参数。

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

与解构赋值结合使用

参数默认值可以与解构赋值的默认值结合使用。

function foo({
    
    x,y=5}){
    
    
    console.log(x,y)
}

foo({
    
    })	//undefined,5
foo()	//error: cannot read property 'x' of undefined
foo({
    
    x:1})	// 1,5
foo({
    
    x:1,y:2})	//1,2

来看看下面两种形式:

function f1({
    
    x,y} = {
    
    x:0,y:0}){
    
    
    return [x,y];
}
function f2({
    
    x=1,y=1} = {
    
    }){
    
    
    return [x,y];
}

f1()	//[0,0]
f2()	//[0,0]

f1({
    
    x:3})	//[3,undefined]
f2({
    
    x:3})	//[3,1]

f1({
    
    })	//[undefined,undefined]
f2({
    
    })	//[1,1]

参数默认值的位置

如果定义了默认值的参数在函数的最尾部,则可以省略;如果非尾部的参数设置默认值,实际上这个参数无法省略,必须以undefined填充。

function foo(x,y=1,z){
    
    
    return [x,y,z]
}

foo();	//[undefined,1,undefined]
foo(1,2,3)	//[1,2,3]
foo(1,,2)	//error
foo(1,undefined,2)	//[1,1,2]

失真的length

指定默认之后,函数的length方法将只返回没有默认值的参数个数,也就是length将失真。这是因为length的本意是该函数预期传入参数的个数。

(function(x,y,z=1){
    
    }).length = 2	//参数个数为3,但返回2

作用域

一旦设置了默认值,函数声明初始化的时候,参数就会形成一个单独的作用域,等到初始化结束,作用域就会失效。

var x=1;
function foo(x,y=x){
    
    
    return [x,y]
}

f(2)	//[2,2]

以上例子中,由于函数设定了默认值,在函数内部会形成单独的作用域,因此函数中的y=x中的x指的是参数的x,而不是全局变量x。

rest参数

ES6引入了rest参数...rest来获取函数多余的参数,这样就无需引用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。

function add(...num){
    
    
    let sum = 0;
    for(var item in num){
    
    
        sum += item;
    }
    return sum;
}

add(1,2,3,4,5)	//15

注意:函数的length属性并不包括rest 的参数个数


尾调用优化

啥叫尾调用啊?

尾调用,顾名思义,就是指某个函数的最后一步是调用另一个函数。

function f(x){
    
    
    return g(x)
}


//以下都不属于尾调用
function f(x){
    
    
    let y = g(x);
    return y;
}

function f(x){
    
    
    return g(x) + 1;
}

function f(x){
    
    
    g(x);
}

尾调用的标志是,必须是在最后调用函数,而不是操作调用后的结果。

尾调用优化

我们知道,函数调用会在内存形成一个“调用记录”, 又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数 A 的内部调用函数 B,那么在 A 的调用帧上方还会形成一 个 B 的调用帧。等到 B 运行结束,将结果返回到 A, B 的调用帧才会消失。 如果函数 B 内部还调用函数 c,那就还有一个 c 的调用帧,以此类推。所有的调用帧就形成一个“调用栈”(call stack)。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、 内部变量等信息都不会再用到了,直接用内层函数的调用帧取代外层函数的即可。

function f(){
    
    
    let x = 1;
    let y = 2;
    return g(x+y);
}

//等同于
g(3);

上面的代码中,如果不是尾调用,函数f就需要保存变量x,y的值、g的调用信息等。但由于尾调用,函数f就结束了,所以内存中可以删除f的调用记录,只保留g的调用帧。这就是尾调用优化,可以大量节约内存。

尾递归

递归是一种非常消耗内存的方法,因为其需要保存大量的调用帧,容易发生内存溢出等错误,但尾递归就很好的解决了这个问题,因为他永远只占用一个调用帧。

// 阶乘函数
function factorial(n){
    
    
    if(n == 1){
    
    
        return 1;
    }else{
    
    
        return n*factorial(n-1);
    }
}
// 我们尾递归真的太厉害了(老财富密码了)
// 普通递归,不好用!尾递归,好用!
function factorial(n,total){
    
    
    if(n == 1){
    
    
        return total
    }else{
    
    
        return factorial(n-1,n*total); 
    }
}

//fibonacci数列
function fibonacci(n){
    
    
    if(n <= 1){
    
    
        return 1
    }else{
    
    
        return fibonacci(n-1) + fibonacci(n-2);
    }
}
// 普通递归,不好用!尾递归,好用!
function fibonacci(n,ac = 1,ac = 1){
    
    
    if(n <= 1){
    
    
        return ac2;
    }else{
    
    
        return fibonacci(n-1,ac2,ac1+ac2);
    }
}

尾递归改写

上面的代码似乎不是很合逻辑,为什么我算一个阶乘还要传一个total进去呢,难道就不能直接给我结果吗?答案是可以的。别忘了,ES6给我们提供了默认值操作!

function factorial(n,total = 1){
    
    
    if(n == 1){
    
    
        return total
    }else{
    
    
        return factorial(n-1,n*total); 
    }
}

factorial(5) 	//120

还有一种尾调用的方法,就是在尾递归函数外再提供一个正常形式的函数来进行尾递归:

function tailFactorial(n,total){
    
    
    if(n == 1){
    
    
        return total
    }else{
    
    
        return factorial(n-1,n*total); 
    }
}

function factorial(n){
    
    
    return tailFactorial(n,1);
}
factorial(5);	//120

这其实是一种柯里化思维。等会儿!啥叫柯里化???

柯里化

函数编程中有一个重要的概念,叫做柯里化,意思是将多参数的函数转化成单参数的形式。

function curry(fn,n){
    
    
    return function(m){
    
    
        return fn.call(this,m,n);
    }		//返回一个可执行函数
}

function tailFactorial(n,total){
    
    
    if(n == 1){
    
    
        return total
    }else{
    
    
        return factorial(n-1,n*total); 
    }
}

const factorial = curry(tailFactorial,1);
factorial(5)	//120

猜你喜欢

转载自blog.csdn.net/yivisir/article/details/107579933