函数的拓展

函数的拓展

函数参数的默认值

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