第七章 函数的扩展

7.1函数参数的默认值

7.1.1基本用法:

在es6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
es6
允许函数的参数设置默值,会直接写在参数定义的后面。

function log(x,y='word'){
console.log(x,y)

}
log('hello')//hello  word
log('hello','a')//hello a

参数变量是默认声明的,就不能用let或者const再次声明。会报错。
使用参数默认值时,函数名不能有同名参数。
默认参数值不是传值的,而是每次都重新计算默认值表达式的值,就是参数默认值是惰性求值的。

7.1.2与解构赋值默认值结合使用

只有当函数的参数是一个对象时,变量x,y才会通过解构赋值而生成,付过函数调用时参数不是对象时,变量就不会生成,而是会报错。另外只有某个参数设置默认值后,后面又没有重新赋值,默认值才会生效。如下》

function  foo({x,y=8}){
console.log(x,y)
}
foo({})//undefined,8
foo({x:1})//1,8
foo({x:1,y:0})//1,0
foo()//报错 

7.1.3

通常,定义了了默认值的参数应该是函数的尾数,因为这样比较容易看出到底省略了哪些参数,如果非尾部的参数设置默认值,实际上这个参数是·无法省略的。

例子:
function(x=1,y){
return[x,y];
}

f()//[1,undefined];
f(2)//[2,undefined];
f( ,1)//报错
f(undefined,1)//[1,1]
例子二:
function f(x,y=6,z){
return [x,y,z];
}
f()//[undefined,6,undefined]
f(1)//[1,6,undefined];
f(1, ,2)//报错
f(1,undefined,2)//[1,6,2]
在上面中,有默认参数值都不是尾参数,这时,无法省略该参数而不省略其后的参数,除非显示输入undefined,就触发默认值。null没有这个效果。
如果传得是null,打印的也会是null。

7.1.4函数的length属性

指定了默认值之后,函数的length属性将返回没有指定默认值的参数个数,也就是说,指定默认值之后,length的属性将会失效。

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

7.1.5作用域

一旦设置了参数的默认值,函数进行声明初始化的时候,参数会形成一个单独的作用域。等到初始化结束时,这个作用域就会消失,这种语法行为在不设置默认值的时候是不会出现的。

var x=1;
function f(x,y=x){
console.log(y)

}

f(2)//2;

上面代码中,参数y的默认值是x,调用函数y 的时候,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出的是2.

7.2rest参数

es6引入了rest参数(形如"...变量名"),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。
rest参数中的变量代表一个数组。所以数组特有的方法都可以用于这个变量。
函数的length不包括rest参数。rest参数只能是最后一个参数,否则会报错;

7.3严格模式。

ES6规定只要函数参数使用了默认值、解构赋值或者扩展运算符。那么函数内部就不能显示设定为严格模式,否则会报错。
是因为函数内部的严格模式同时适用于函数体和函数参数。但是,还是执行时候,先执行函数参数,然后在执行函数体,这样就有一个不合理的地方,只有函数体之中才能知道参数是否应该以严格模式执行,但是参数却先与函数体执行。
但是有两种方法可以规避这种限制。
第一种是设定全局的严格模式,这是合法的
第二种就是把函数包在一个无参数的立即执行函数里面。

7.4name属性

函数的name属性返回的是函数的函数名。
如果讲一个匿名函数赋值给一个变量,es5返回的是一个空字符串,es6返回的name属性则会是是实际的函数名。
如果讲一个具名函数赋值给一个变量,es5和es6的name属性都返回的的这个具体的函数名的名字。
Function构造函数返回的函数实例,name的属性的值为anonymous。

(new Function).name=anonymous


foo.bind({}).name//"bound foo"

(function(){}).bind({}).name//"bound"

bind返回的函数,name属性值会加上bound前缀。

7.5箭头函数

7.5.1基本用法

es6允许使用箭头函数

var f=v=>v;
上面的代码等于以下的代码
var f=function(v){
return v;
}

如果箭头函数不需要参数或者需要多个参数的话,就是用圆括号代表参数部分。

var  f=()=>5;
等同于
var f =function(){
return 5
}
var sun =(num1,num2)=>num1+num2;
等于
var sun =function(num1,num2){
return num1+num2;
}

如果箭头函数的代码块部分多于一条语句,就要使用大括号将其括起来,并使用return 语句返回。

var sum =(num1,num2)=>{return num1+num2}

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。

var get=id=>({id:id,name:"Temp"})

箭头函数可以跟变量解构结合使用。

const full=({first,last})=>first+''+last'
等同于
function full(person){
return person.first+''+person.last;      
}

箭头函数的一个用处就是简化回调函数。

正常函数写法
[1.2.3].map(function(x){
return x*x;
}
)
箭头函数写法
[1.2.3].map(x=>x*x)


下面是rest参数与箭头函数结合的例子

const number=(...nums)=>nums;
numnber(1,2,3,4,5)//1,2,3,4,5


const headTil=(head,...tail)=>[head ,tail]

headTil(1,2,3,4,5)//[1,[2,3,4,5]]

7.5.2注意事项

1.函数体内的this对象就是定时所在的对象,而不是使用时所在的对象。
2.不可以当做构造函数,也就是到货,不可以使用new命令,否则会抛出一个错误。
3.不可以使用arguments对象想,该对象在函数体内不存在,如果要用们可以用rest参数进行代替。
4.不可以使用yield命令,因此箭头函数不能用作Generator函数。

es5 中this的指向是可变的,但是在箭头函数中他是固定。除了this,一下3个函数在箭头函数中也是不存在的分别指向外层函数的对应变量:arguments super 和new.target。
另外,由于箭头函数没有自己的this。当果然也不能用call() /apply()/bind()这些方法改变this 的指向。

7.5.3嵌套的箭头函数

箭头函数还可以嵌套箭头函数。

下面是一个部署管道机制的例子,就是前一个函数的输出值是后一个函数的输入值。
const pipeline=(...funcs)=>
val=>funcs.reduce((a,b)=>b(a),val));
const plus1=a=>a+1;
const plus2=a=>a*2;
const  add=pipelline(plus1,plus2);


add(5);//12

还可以写成以下的形式
const plus1=a=>a+1;
const plus2=a=>a*2;
plus2(plus1(5))//12

7.6绑定this

箭头函数可以绑定this的对象,减少了显示绑定this对象的写法,call apply bind

函数绑定运算符是并排的双冒号::。双冒号左边是一个对象,右边是一个函数,该运算符会自动将左边的对象作为上下文环境绑定到右边的函数。

foo::bar ;
等同于、
bar .bind(foo)


如果双冒号左边是空的,,右边是一个对象,则等于将该方法绑定在对象上。


var method=obj::obj.foo
等于
var method=::obj.foo


let log =::console.log;
等于:
var log =console .log.bind(console);

由于双冒号运算符返回的是还是原对象,因此可以采用链式写法。

7.7尾调用优化

7.7.1

尾调用是函数式编程的一个重要概念,非常简单,就是指某个函数的最后一步是调用另一函数。


funcrion f( x){
return g(x);
}
上面代码中,函数f的最后一步是调用函数g,就叫做尾调用。

funcrion f( x){
let y=g(x);

return y;
}

因为最后一步的是赋值,所以不属于尾调用。,调用后还有操作的都不属于尾调用。

7.7.2尾调用优化

,尾调用之所以跟其他的不提用,就是因为他的特位置、。
只有不再用到外层函数的内部变量,内层函数的调用帧才会取到外层函数的调用帧,否则就无法进行"尾调用优化"

你可能感兴趣的:(第七章 函数的扩展)