函数的拓展
函数参数的默认值
ES5
function log(x,y){
if(typeof y==='undefined'){
y='world'
};
console.log(x,y)
}
log("hello")//hello world
ES6
function log(x,y='world'){
console.log(x,y)
}
通常情况下,定义了默认值的参数应该是函数的尾参数,这样比较容易看出到底省略了哪些参数。
函数的 length 属性
指定了默认值以后,函数的 length 属性将返回没有指定默认值的参数个数。length 的含义是函数与其传入的参数个数,同理,rest 参数不会计入 length 属性
(function(a){}).length //1
(function(a=5){}).length //0
(function(...args){}).length//0
应用
利用参数默认值,可以指定某一个参数不可省略,省略则抛出错误
function throwIfMissing(){
throw new Error('Missing Params')
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided;
}
foo()
// Uncaught Error: Missing Params
rest 参数
rest 参数用于获取函数的多余参数,用来替代 arguments 对象
function add(...values){
let sum = 0;
for(var val of values){
sum+=val
}
return sum
}
add(1,2,3)//6
es5 arguments
var sortNumbers = function(){
return Array.prototype.slice.call(arguments).sort()//slice可以转化类数组对象
}
es6 rest
const sortNumbers=(...numbers)=>numbers.sort()
sortNumbers(3,7,4,5)//[3,4,5,7]
拓展运算符
拓展运算符是三个点(…)。是 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列;
console.log(1,...[1,2,3],4)
//1 1 2 3 4
该运算符主要用于函数调用
function push(array,...items){
array.push(items)
}
push([1,2,3],4,5)//1,2,3,4,5
function add(x,y){
return x+y
}
var numbers = [4,38];
add(...numbers);//42
替代数组的 apply 的写法
var arr1 = [0,1,2]
var arr2 = [3,4,5]
es5
Array.prototype.push.apply(arr1,arr2)//[1,2,3,4,5]
es6
arr1.push(...arr2)//[1,2,3,4,5]
合并数组的新写法
es5
[1,2].concat(more)
es6
[1,2,...more]
与解构赋值结合生成数组
es5
a = list[0],rest = list.slice(1)
es6
[a,...rest] = list
如果将拓展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
转化类数组对象
var nodelist = document.querySelectorAll("div");
var array= [...nodelisst];
Map 和 Set 解构,Gennerator 函数
[...map.keys()]
[...generator()]
name 属性
函数的 name 属性返回该函数的函数名
function foo(){}
foo.name //"foo"
var func1 = function(){}//"func1"
(new Function).name //"anonymous"
箭头函数
es5
var f = function(v){
return v
}
es6
var f = v => v
注意: 1.箭头函数没有 this,内部的 this 就是外部代码块的 this,因为没有 this 所以不能用作构造函数
2.箭头函数不能使用 arguments 对象,可以使用 rest 参数代替
3.箭头函数不能用作 Generator 函数
es5
function foo(){
this.id = 1;
setTimeout(function(){
console.log(this.id)//undefined this指向window
},1000)
}
foo.call({id:1})
es6
function foo(){
setTimeout(()=>{
console.log(this.id)//1 指向调用的对象
},1000)
}
foo.call({id:1})
或者可以改写为
function foo(){
var _this = this;
setTimeout(function(){
console.log(_this.id)
},1000)
}
除了 this 之外,以下 3 个变量在箭头函数中也不存在,分别指向外层函数的变量
arguments super new.target
也不能用 call(),apply(),bind()来改变 this 的指向
函数绑定
函数绑定运算符是(::)左边对象右边函数。运算符会自动将左边的对象作为上下文环境(this)绑定到右边的函数上。
foo::bar;
//等同于
bar.bind(foo)
foo::bar(...arguments)
//等同于
bar.apply(foo,arguments)
尾调用优化 尾递归
尾调用是某个函数的最后一步是调用另一个函数
function f(x){
return g(x)
}
尾调用不一定出现在函数尾部。只要是最后一步操作即可
function f(x){
if(x>0){
return m(x)
}
return n(x)
}//m n 都属于尾调用
函数调用会在内存形成调用帧,保存调用位置和内部变量等信息。如果在函数 A 的内部调用函数 B,那么 A 的调用帧上方还会形成一个 B 的调用帧,等到 B 运行结束将结果返回到 A,调用帧才会消失。
所有的调用帧形成一个调用栈。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置,内存变量等信息都不会再用到了。可以直接用内存的调用帧取代外层函数的调用帧。
只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧。否则无法进行尾调用优化。
//递归函数的改写
function factorial(n){
if(n===1) return 1;
return n*factorial(n-1)
}
factorial(6) //720
function factorial(n,total=1){
if(n===1) return total;
return factorial(n-1,total * n)
}
factorial(6) //720