函数参数默认值

(1) es5中模拟参数默认值:

function fn(name, count){
    name = name || 'xiao ming'
    count= count|| 1
    console.log(name, count)
}
复制代码

这样可以为没有传入的参数,设置为默认值。这种方式的问题:如果count传入0,即使这个值是合法的,但也会被设置为默认值1。 改进后:

function fn(name, count){
    name = typeof name !== 'undefined' ?  name : 'xiao ming'
    count = typeof count !== 'undefined' ?  count : 1
    console.log(name, count)
}
复制代码

(2) es6的做法:

function fn(name = 'xiao ming', count = 1){
   console.log(name, count)
}
// name使用默认值
fn(undefined, 18)
复制代码

扫盲: null是一个有效参数,传入null并不会使用默认参数

(3) 命名参数与arguments的关系

es5非严格模式下,修改命名参数,会同步更新到arguments对象中; 而在es6或者严格模式中,命名参数和arguments是分离的

function fn1(name, count) {
    name = 'li si'
    count = 5
    console.log(arguments[0])
    console.log(arguments[1])
}
fn1('xiao wang', 4)
// 非严格模式下
// li si
// 5

// 严格模式下或es6环境中
// xiao wang
// 4
复制代码

(4) 函数默认表达式

function count(){return 6}
function fn1(name, count = count()) {
    console.log(name)
    console.log(count)
}
fn1('xiao wang')
// 'xiao wang'
// 5
复制代码

扫盲: 1. 参数、参数默认值、参数默认表达式都在同一个独立的作用域内; 2. 参数作用域中存在临时性死区,即后面的参数可以引用到前面的参数,反之不可以; 3. 参数作用域引用不到函数体内的变量

证明独立作用域的例子:

function fn(a, cb = () => { a.b = 1; console.log(a) }) {
    var a = { a: 1 }
    cb()
    console.log(a)
}
fn({ c: 3 })
// { c: 3, b: 1 }
// { a: 1 }
复制代码

cb执行时,获取到的对象a并不是函数体内新定义的,而是传入的参数a,所以他们是在同一独立作用域的

无名参数

(1) es5中使用arguments获取不定数量的参数

// 如果没有注释,很难发现函数需要传入无命名参数
function fn(arr){
    // i 从 1开始,需要跳过 arr这个命名参数
    for(let i = 1, len = arguments.length; i < len; i++){
        arr.push(arguments[i])
    }
    return arr;
}
console.log(fn([3],4,5,6,7))  // [3,4,5,6,7]
复制代码

存在的问题:

  1. 并不容易发现函数,需要传入不定量参数
  2. 如果有命名参数,arguments中既包含命名参数也包含无名参数,还需要拆分获取

(2) es6中,使用不定参数

在参数前加三个点...,表明这是一个不定参数,这个参数为数组,包含着自它以后传入的参数。 并且解决了上述es5中的问题

function fn(arr, ...items){
    for(let i = 0, len = items.length; i < len; i++){
        arr.push(items[i])
    }
    return arr;
}
console.log(fn([3],4,5,6,7))   // [3,4,5,6,7]
复制代码

不定参数的限制:

  1. 每个函数只能定义一个不定参数,而且只能处于所有参数末尾。错误示范:function fn(a, ...b, c){}
  2. 对象的setter函数不能使用不定参数。错误示范:let obj = {set name(...value){}}

扫盲:

  1. 无论是否使用不定参数,arguments总是包括所有传入的参数
  2. 函数的**length属性**,表示命名参数的数量,跟不定参数无关

函数的name属性

  1. function fn(){} name为fn
  2. let fn = function (){} name为fn
  3. let fn = function fn1(){} name为fn1因为函数名比被赋值的变量权重高
  4. let obj = {get firstName(){}, sayName(){}}obj.firstName为getter函数,所以name为get firstNameobj.sayNamename为sayName
  5. 通过bind()创建的函数,name会带有bound前缀。fn.bind()name为bound fn
  6. 通过Function()创建的函数,name为anonymousnew Function() name为anonymous

扫盲:函数name属性不一定能获取对函数的引用,它只是协助调试用的额外信息

函数的多重用途

箭头函数

相关语法就不再赘述

与普通函数的区别:

  • 没有this、super、arguments绑定,这些值都由外层的非箭头函数决定
  • 不能使用new关键词调用;没有原型;函数体内没有new.target。因为箭头函数没有[[construct]]方法,所以不能当做构造函数调用。
  • 不可以改变this指向。箭头函数的this指向,只跟函数定义时的环境有关,跟使用环境无关,call apply bind也无力改变
  • 再也不用缓存this了,再也不会有_this self that等各式各样的变量了。
  • 说到这里,突然想到了箭头函数可以在react中绑定事件处理函数的this到组件实例 ,以及 普通函数的this与执行环境相关
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  • 不支持重名参数。es6与es5严格模式下都不支持函数参数重名。
// es5非严格模式下,参数重名情况
 function fn(a,b,a){
   console.log(a)
   console.log(b)
   console.log(arguments[0])
   console.log(arguments[1])
   console.log(arguments[2])
   a = 4
   console.log(arguments[0])  
   console.log(arguments[1]) 
   console.log(arguments[2]) 
 }
 fn(1,2,3)
//3
//2
//1
//2
//3
//1
//2
//4
// 即 arguments[0]与 a解除了相关性
复制代码

双冒号运算符

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

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

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
复制代码