ES6-----函数

一、 函数默认值

在es5时如果我们想要给函数参数一个默认值,一般是通过 || 运算符进行判断,那么假如希望有这样的函数,只有当不传值或者传undefined 时,才会取默认值,此时可以用以下这种方式:

function fruit(name) {
    name = name === undefined ? 'none' : name;
    return name;
}
console.log(fruit('orange'));//orange
console.log(fruit());//none
console.log(fruit(undefined));//none
console.log(fruit(''));// 
console.log(fruit(null));//null
console.log(fruit(0));//0
console.log(fruit(false));//false

但是上面这种写法不是很好,如果函数有多个参数,每个都想这样取默认值,就要写很多遍。ES6提供了一种更简洁的方式,来给参数设置默认值。

//函数默认值
//相当于 this.name = name === undefined ? 'none' : name;
//也就是只有当参数绝对等于undefined的时候才会取默认值
function fruit(name = "none") {
    return name;
}
console.log(fruit('orange'));//orange
console.log(fruit());//none
console.log(fruit(undefined));//none
console.log(fruit(''));// 
console.log(fruit(null));//null
console.log(fruit(0));//0
console.log(fruit(false));//false

通过上面运行结果,可以看出只有当参数绝对等于undefined的时候才会取默认值
如果有多个参数,设置了默认值的参数叫可选参数,没有设置默认值的参数叫必需参数。一般会把可选参数放到最后,这样比较容易看出来,到底省略了哪些参数。

//m:必需参数  n:可选参数
function add(m, n = 0) {
    console.log(m + n);
}
add(1, 2);//3
add(1);//1

//如果把可选参数放在中间,而非最后,需要在调用时明确传入undefined,因为它没办法省略
function add2(m, n = 0, t) {
   console.log(m + n + t);
}
add2(1, undefined, 2);//3

在es5非严格模式下,函数参数的变化会体现在arguments上,也就是实参变化,arguments也会随之变化,两者是一一对应的。但是在es5严格模式下,无论参数如何变化,arguments对象不再随之变化,两者间互不影响。

//es5严格模式下 实参改变并不影响argumnets
function mixArgs(first, second) {
    "use strict"
    console.log(first, arguments[0]);//1  1
    console.log(second, arguments[1]);//2 2
    first = "c";
    arguments[1] = "d";
    console.log(first, arguments[0]);//c 1
    console.log(second, arguments[1]);//2 d
}
mixArgs(1, 2);

在es6中,如果一个函数使用了默认参数值,则无论是否显示定义了严格模式,arguments对象的行为都将与es5严格模式下保持一致。即参数变化,arguments对象不再随之变化,两者间互不影响

function mixArgs(first, second = "b") {
    console.log("length:" + arguments.length);//length:1
    console.log(first === arguments[0]);//true
    console.log(second === arguments[1]);//false
    //此时second值为b arguments值为undefined 

    first = "c";
    second = "d";
    console.log(first, arguments[0]);//c 1
    console.log(second, arguments[1]);//d undefined
}
mixArgs(1);

默认参数也可以以表达式的形式存在,比如下面的这个例子:

//默认参数表达式
let initValue = 5;
function getValue() {
   return initValue++;
}
function addFn(first, second = getValue()) {
  return first + second;
}
console.log(addFn(1, 2));//3
console.log(addFn(1));//6
console.log(addFn(1));//7

/*在此例中,initValue默认值是5,每次调用getValue的时候加1。第一调用addFn方法时,由
于明确给第二个参数传了2,所以不会取默认值,此时的getValue方法未执行,initValue仍然
是5;第二次调用addFn方法,只传了一个参数,second需要取默认值,getValue方法执行,方
法返回6,initValue变成6;第三次调用addFn方法,second仍需要取默认值,而由于上次的执
行,此时的initValue值为6,执行getValue方法后second值为6,方法返回7,initValue为7
*/

从上面这个例子中可以看出,预编译时不会调用getValue()方法,只有当调用addFn函数且不传第二个参数的时才会调用。这里就涉及到了惰性求值的概念。如果给设置了默认值为表达式的参数传了值,那么该参数的求默认值部分就不会执行。
由于默认参数是在函数调用的时候求值,所以可以使用先定义的参数作为后定义的参数的默认值。

function addFn(first, second = first) {
    return first + second;
}
console.log(addFn(1, 1));//2
console.log(addFn(1));//2

在上面这个例子中second的默认值是first,如果只传入一个参数,则两个参数的值相同。有一点需要注意,在引用参数默认值的时候,只允许引用前面参数的值,即先定义的参数不能访问后定的参数。我们来修改一下上面的这个例子:

function addFn(first = second, second) {
    return first + second;
}
console.log(addFn(1, 1));//2
console.log(addFn(undefined, 1));//ReferenceError: second is not defined

调用addFn(undefined, 1)会抛出错误,这是因为first的默认值是second,而second又比first晚定义,不能作为first的默认值。其实这里也涉及到了临时死区。在执行addFn(undefined,1)以及addFn(1,1)的时候相当于在引擎的背后分别做了如下的事情:

//调用addFn(undefined,1)时
let first = second; //此时的second仍处于TDZ中,不能使用
let second = 1;

//调用addFn(1,1)时
let first = 1;
let second = 1;

这里需要注意的是参数的默认值不可以取函数体内声明的变量

function addFn(first, second = value) {
    let value = 4;
    return first + value;
}
console.log(addFn(1));//ReferenceError: value is not defined

现在来总结一下:
1. es6中可以给函数设置默认值,但是只有在参数值绝对等于undefined的时候才会取默认值
2. 如果函数参数存在默认值,参数的变化,arguments不会随之改变,两者互不影响
3. 参数默认值可以以表达式的形式存在,但是只有在函数执行且参数需要有默认值的情况下表达式才会执行;否则不会执行(惰性求值)
4. 如果参数默认值需要引用其他参数,只能引用在它前面的参数的值,即先定义的参数不能引用后定义的参数
5. 默认值不能取函数内部声明的变量

二、 不定参数

在函数的命名参数前添加三个点(…)就表示这个是一个不定参数,该参数为一个数组,包含着子它之后传入的所有参数,通过这个数组名即可以逐一访问里面的参数。

function pick(object, ...keys) {
    console.log(keys.length, keys);//2  ["a", "year"] 
    console.log(arguments.length, arguments[1], arguments[2]);//3 "a" "year"
    console.log(pick.length);//1  函数的属性length计算时不会算不定参数
    let result = Object.create(null);
    for (let i = 0, len = keys.length; i < len; i++) {
        result[keys[i]] = object[keys[i]];
    }
    return result;
}
let obj = {
    a: "test1",
    b: "test2",
    year: 2018
}
let data = pick(obj, "a", "year");
console.log(data); //{a: "test1", year: 2018}

在上面这个函数中,keys中包含的是object之后传入的所有参数,而arguments对象包含的是所有参数。如果想要对keys中的值进行处理直接遍历就可以了,无需裁剪。
在使用不定参数时有两点需要注意:
1)每个函数最多只能声明一个不定参数,而且只能放在最后
2)不定参数不能用于对象字面量setter中

在es6中有一个新功能与不定参数十分形似,叫做展开运算符。 不定参数可以让你指定多个独立的参数,并通过整合后的数组来访问;但是展开运算符正与之相反,它可以让你指定一个数组,将他们打散后作为各自独立的参数传入函数。利用展开运算符可以很简单的解决一些问题。比如下面的这两个例子:

//eg1:获取一个数组中的最大值:
    let values = [23, 5, 78, 100];
    //以往方法
    console.log(Math.max.apply(Math, values));
    //使用展开运算符
    console.log(Math.max(...values));//100

//eg2:连接两个数组
    let arr1 = [1, 2, 3];
    let arr2 = [4, 5, 6];
    //以往方法
    console.log(arr1.concat(arr2));//[1, 2, 3, 4, 5, 6]
    //使用展开运算符
    console.log([...arr1, ...arr2]);//[1, 2, 3, 4, 5, 6]
三、 箭头函数

箭头函数的基本结构是:() => {} ,左边是参数部分,右边是函数体部分,比如:

//原来定义函数的方式:
function getValue(num1, num2) {
    return num1 + num2;
}

//箭头函数:
let getValue = (num1, num2) => { return num1 + num2 };

如果函数体中只有一条语句,可以不写{}

let getValue = (num1, num2) => num1 + num2;//不需要加return

如果只有一个参数,可以省略()

let returnValue = value => value; 
//相当于
function returnValue(value) {
    return value;
}

如果声明一个空函数:

let fn = () => { }; //相当于:function fn() { }

在箭头函数体中只有一条返回对象的语句,需要在对象外加上()

let objFn = (name, value) => ({ name, value });
console.log(objFn('lily', 18));//{name: "lily", value: 18}

立即执行函数

let num = ((num1, num2) => num1 + num2)(1, 3);//不能将(1,3)放在前面的括号里
console.log(num);//4
//相当于:
let num = (function (num1, num2) {
    return num1 + num2;
})(1, 3);

箭头函数的语法基本上就是上面这些,此外它还有一些自己的特点:
1. 箭头函数中没有this、arguments、super和new.target(用于解决判定函数是否通过new关键字调用的问题)绑定,箭头函数中的this绑定外层最近的非箭头函数中的this
2. 箭头函数不能作为构造函数使用,不能通过new关键字调用,没有原型。

let arrowObj = {
    name: 'arrowObj',
    getName: function (age) {
        this.age = age;
        let show = () => { console.log(this.name, arguments[0]) };
        //箭头函数中的this绑定了外层非箭头函数getName中的this
        //arguments是外层非箭头函数getName中的arguments
        show();
    }
}
arrowObj.getName();//arrowObj  13

箭头函数无法作为构造函数使用,如果试图通过new关键字调用,会报错

let arrowFn = name => name;
new arrowFn();//TypeError: arrowFn is not a constructor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值