ES6函数新增内容介绍

前言

学习函数新增内容,需要先了解ES6的变量解构赋值。

本文大量引用阮一峰老师的ES6手册。

为函数的参数设置默认值

function Point(x = 0, y = 0) {
  this.x = x;
  this.y = y;
}

const p = new Point();
console.log(p);

注意事项:

  1. 函数内部不允许给参数重复声明,比如用var、let、const声明。但可以重复赋值。
  2. 参数默认值不是传值的,而是每次都重新计算默认值表达式的值,即时从前计算过,也当做没计算过。也就是说,参数默认值是惰性求值的。

参数默认值跟解构赋值配合使用

首先你要懂ES6变量解构赋值。

下面是利用对象的解构赋值,函数声明的参数模式,必须与传入值的模式匹配:

function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined 5 虽然是空对象,但是模式是匹配的
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined 传入undefined,模式不匹配,所以报错

如果想要什么参数都不传,也依然不报错,怎么做?依然是利用解构赋值,下面代码中,{x, y = 5} = {}表示如果整个参数不存在,就默认为空对象,然后再计算x和y各是多少,x因为没有对应值,当然是undefined,y虽然也没有对应值,但是有默认值,所以y是5。

function foo({x, y = 5} = {}) {
  console.log(x, y);
}

foo() // undefined 5

对比下面两段代码,它们的结果是一样的。区别在哪?

写法一:

  1. 没有传参,所以用默认参数,也就是空对象。
  2. x和y先去空对象寻找对应值,找不到,所以用自己的默认值。

写法二:

  1. 没有传参,所以用默认参数{ x: 0, y: 0 }
  2. x和y去{ x: 0, y: 0 }寻找对应值,找到了对应值,所以直接用对应值。

区别就是在哪一步设默认。所以,在任意一步设默认都可以,只要是合法的js代码。

// 写法一
function m1({x = 0, y = 0} = {}) {
  return [x, y];
}
console.log(m1());

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}
console.log(m2());

参数默认值应该放到参数队列最后面

这么做的目的是可以省略若干传参。如果参数默认值排在前面,没有默认值的参数反而在后面,那么传参的写法就会很不科学,比如:

function f(x, y = 5, z) {
  return [x, y, z];
}

怎么传参?

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错 这种最简练的写法是错误的
f(1, undefined, 2) // [1, 5, 2] 这种写法虽然正确,但是把undefined传进去代码很丑

所以,如果参数有默认值就应该放到参数队列最后面。

参数默认值有特殊作用域

var x = 1;

 // 2作为值传给x,由于y = x是完全独立的作用域,所以y的值是参数x的值,也是2
function f(x, y = x) {
  console.log(y); // 打印2
}

f(2) // 2
let x = 1;

 // 没有传入值,所以y取默认值,y=x形成独立作用域,所以y是x的值,x指向外层的1,也就是y是1。
function f(y = x) {
  let x = 2; // 这个x不影响y的值
  console.log(y); // 打印1
}

f() // 1

还有更复杂的情况,这里不多介绍了,更复杂的情况可能只会出现在面试题里,而实践中,请让自己的代码条理清晰,这样对自己,对别人,都有好处。

参数默认值的一个应用

先定义一个通用函数,不干别的,只负责报错:

function throwIfMissing() {
  throw new Error('Missing parameter');
}

然后,其他函数里面如果有不允许省略的参数,就赋值为这个函数:

function throwIfMissing() {
  throw new Error('Missing parameter');
}

function foo(a, b, c = throwIfMissing()) {

}

foo(1,2); // Uncaught Error: Missing parameter

rest参数

学习变量解构赋值的时候,我们就遇到了rest变量,也就是:

let [a, b, ...c] = [1,2,3,4,5,6,7];
console.log(c); // [3,4,5,6,7]

参数也有这种写法,表示剩余的传参,有多少我全包了。

function add(...values) {
  let sum = 0;

  for (var val of values) { // values 是一个数组,包含所有传入的参数
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

所以,到现在,传参真的可以为所欲为,根本不再是ES5时代的参数必须一对一:

  1. 你有一系列参数,我用比如...args就可以打包。
  2. 如果你传入一个数组,我用解构赋值就可以打散。

箭头函数

箭头函数是ES6对函数写法的最大修改,改到人们一开始都不认识。

var f = v => x;
// 等价于
var f = function(v) {
  return x;
};

代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

var sum = (num1, num2) => num1 + num2;
// 等价于
var sum = function(num1, num2) {
  return num1 + num2;
};

由于大括号是被默认解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

箭头函数可以与变量解构结合使用:

const full = ({ first, last }) => first + ' ' + last;
// 等价于
function full({ first, last }) {
  return first + ' ' + last;
}

// 使用函数的时候就full({first: 4, last: 8});就可以了

箭头函数写法的优势:

  1. 简练。定义一个判断是偶数的函数如下,因为这个函数就是参数跟运算,所以箭头函数很简练:
const isEven = n => n % 2 == 0;
  1. 简化回调函数。ES5时代,为了清洗的写回调函数,往往要折行写代码,现在就简化了。
// 正常函数写法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭头函数写法
[1,2,3].map(x => x * x);


// 正常函数写法
var result = values.sort(function (a, b) {
  return a - b;
});

// 箭头函数写法
var result = values.sort((a, b) => a - b);

注意,箭头函数不是永远等价于常规写法。区别如下:

  1. 常规写法中,this对象的指向是可变的,但是在箭头函数中,它是固定的。这也是ES6为了降低js学习难度所做的改变。常规写法中,函数体内的this对象,不一定是定义时所在的对象,而是使用时所在的对象。但是,箭头写法中,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

  3. 不可以使用arguments对象,该对象在函数体内根本不存在。如果要用,可以用 rest 参数代替。

  4. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

你可能感兴趣的:(ES6函数新增内容介绍)