函数是所有编程语言的重要组成部分。
在ES5中模拟默认参数。
<script>
function makeRequest(url,timeout,callback){
timeout = timeout || 2000;
callback = callback || function(){}
// 函数其余部分
}
</script>
这个方法呢也有一定的缺陷,如果我们想给makeRequest函数的第二个形参timeout传值0时,会被认为是一个假值,并最终将timeout赋值为2000.在这种情况下,可以通过typeof
来检查参数类型。
即代码:
<script>
function makeRequest(url,timeout,callback){
timeout = (typeof timeout!=="undefined") ?timeout: 2000;
callback = (typeof callback !== "undefined") ? callback :function(){}
// 函数其余部分
}
</script>
ES6中模拟默认参数
<script>
function makeRequest(url,timeout=2000,callback = function(){}){
// 函数其余部分
}
</script>
url是必须的,其他两个为可选参数。
使用无命名参数可以合理的减少代码量
ES5中无命名参数
javaScript提供auguments对象来检查函数的所有参数,而不必定义每一个参数。
<script>
function pick(object){
let result = object.create(null);
// 从第二个参数开始
for(let i= 1,len=arguments.length;i<len;i++){
result[arguments[i]] = object[arguments[i]];
}
return result;
}
let book = {
title:"living",
author:"YuHua",
year:2015
};
let bookData = pick(book,"author","year");
console.log(bookData.author);
console.log(bookData.year);
</script>
输出结果为
living YuHua
这个函数模仿了Underscore,js库中的pick()方法,返回一个给定对象的副本,包含原始对象属性的特定子集。
关于pick()函数应该注意如下几点:
1.并不容易发现这个函数可以接受任意数量的参数;
2.因为第一个参数为命名参数且被使用,当你查找需要拷贝的属性名称时,不得不从索引1开始遍历arguments对象。
所有在es6中引入不定参数
不定参数
该参数为一个数组,包含着自它之后传入的所有参数,通过这个数组名即可逐一访问里面的参数。
function pick(object,...keys){
let result = Object.create(null);
for(let i= 0,len=keys.length;i<len;i++){
result[keys[i]] = object[keys[i]];
}
return result;
}
不定参数keys包含的时object之后传入的所有参数,而arguments对象包含的则是所有传入的参数,包含Object。
不定参数使用限制
//抛出异常 不定参数后不能有其他命名参数
<script>
function pick(object,...keys,last){
let result = Object.create(null);
for(let i= 0,len=keys.length;i<len;i++){
result[keys[i]] = object[keys[i]];
}
return result;
}
</script>
let object = {
//语法错误 不可以在setter中使用不定参数
set.name(...value)
//其余代码
}
是因为对象字面量setter的参数有且只能有一个。
不定参数对arguments的限制
如果声明函数时定义了不定义参数,则在函数被调用时,arguments对象包含了所有传入函数的参数。
无论是否使用不定参数,arguments
对象总是包含所有传入函数的参数。
增强的Function构造函数
通常用来动态创建新的函数,这种构造函数接受字符串形式的参数,分别为参数和函数体。
let object = {
//语法错误 不可以在setter中使用不定参数
set.name(...value)
//其余代码
}
var pickFirst = new Function("...args","return args[0]");
console.log(pickFirst(1,2));//1
展开运算符
Math.max()是Javascript内置的返回最大值的函数。
它不允许传入数组,所以在ES5中需要手动实现数组遍历取值,或者使用apply方法。
let values = [25,50,75,100];
console.log(Math.max.apply(Math,values)); //100
在ES6中使用展开运算符就可以实现对数组的访问。
let values = [25,50,75,100];
console.log(Math.max(...values)); //100
使用apply方法需要手动指定this的绑定,使用展开运算符就会使代码更简洁。
name属性
函数name属性的值不一定引用同名变量,它只是协助调试用的额外信息,所以不能使用name属性的值来获取对于函数的引用。
JavaScript函数有两个不同的内部方法:[[call]]和[[construct]]。当通过new关键字调用函数时,执行的是[[construct]]函数,它负责创建一个通常被称作实例的新对象,然后再执行函数体,将this绑定到实例上,如果不通过new关键字调用,则执行[[call]]函数,从而直接执行代码的函数体。
具有[[construct]]方法的函数被统称为构造函数。
箭头函数就没有[[construct]]方法。
元属性New,target
为了解决判断函数是否通过new关键字调用的问题,,ES6引入了new.target这个元属性。指非对象属性,可以提供非对象目标的补充信息,例如new。
如果调用[[construct]]方法,new.target被赋值为new操作符的目标,通常是新创建对象实例,也就是函数体内this的构造函数;如果调用[[call]]方法,则new.target的值为undefined。
在函数外使用New.target是一个错误的语法。
箭头函数的极大特点:
[[construct]]
方法,所以不能用作构造函数。var result = values.sort((a,b)=>a-b);
尾调用优化是指函数作为另一个函数的最后一条语句被调用,
function doSomething(){
return doSomethingElse();//尾调用
}
在ES6中如果满足以下条件,尾调用不再创建新的栈帧,而是清楚并重要当前栈帧。(因为在ES5之前需要)
function factorial(n,p=1){
if(n<=1){
return 1*p;
}else{
let result = n*p;
//优化后
return factorial(n-1,result);
}
}