ES6-函数

497 阅读6分钟

1 函数参数的默认值

ES6函数允许将函数参数默认值写在参数定义后面。

1.1 优点:

1)在不看文档和函数体的情况,开发人员知道哪些参数是可以省略的;
2)方便以后代码优化,去掉这个参数也不影响代码运行;

1.2 基本用法

1)参数变量是默认声明的,不需要使用const或let声明参数变量;

var fn = function(x, y = 0) {
   const x = 2;
   console.log(x + y);
}
fn(1); // Uncaught SyntaxError: Identifier 'x' has already been declared

2) 具有默认值参数的函数,该函数不能有同名参数

function fn(x, x, y) {
  console.log(x + x + y);
}
// 报错:Uncaught SyntaxError: Duplicate parameter name not allowed in this context
function fn(x, x, y = 0) {
  console.log(x + y);
}
fn(1, 2, 3);
fn(1, 2);

3)只有传入的参数值为undefined,该参数的默认值(如果有)才能生效

var fn = function(x, y = 'world') {
    console.log(x + y);
}
fn(); // undefined 'world'
fn('Hello'); // Hello world,第二个参数不传,默认传入undefined
fn('Hello', undefined); // Hello world
fn('Hello', 'China'); // Hello China
fn('Hello', ''); // Hello
fn('Hello', null); // Hello null

4)函数参数默认值不是传值,而是每次调用时重新计算,即惰性求值

let a = 1;
function fn(x = a + 1) {
  console.log(x + x);
}
fn(); // 4
a = 2;
fn(); // 6

1.3 与解构赋值一起使用

1)只有当函数fn的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数fn调用时没提供参数,变量x和y就不会生成,从而报错。

在这种情况下,如果不传入一个对象参数,解构赋值失败,报错。

function fn({x, y = 2}) {
  console.log(x + y);
}
fn(); // example-1.html:12 Uncaught TypeError: Cannot destructure property `x` of 'undefined' or 'null'.
fn({x: 1}); // 3
fn({x: 1, y: 3}); // 4

2)通过提供函数参数的默认值,就可以避免上面的情况。

该情况下,参数解构赋值成功,不会报错。

function fn({x, y = 2} = {}) {
  console.log(x, y);
}
fn(); // undefined 2
// 1
function fetch(url, {type, header, data}) {
    console.log(url);
}
fetch('http://api.com/getUserInfo'); // 报错:Uncaught TypeError: Cannot destructure property `type` of 'undefined' or 'null'.
fetch('http://api.com/getUserInfo', {}); // http://api.com/getUserInfo

// 2
function fetch(url, {type, header, data} = {}) {
    console.log(url);
}
fetch('http://api.com/getUserInfo'); // http://api.com/getUserInfo

// 3
function fetch(url, {type, header, data} = {type: 'post'}) {
    console.log(url, type);
}
fetch('http://api.com/getUserInfo'); // http://api.com/getUserInfo post
fetch('http://api.com/getUserInfo', {}); // http://api.com/getUserInfo undefined

// 4
function fetch(url, {type = 'post', header, data} = {}) {
    console.log(url, type);
}
fetch('http://api.com/getUserInfo'); // http://api.com/getUserInfo post
fetch('http://api.com/getUserInfo', {}); // http://api.com/getUserInfo post

1.4 参数默认值的位置

1)函数的尾参数有默认值-该参数可以省略

function fn(x, y = 1) {
    console.log(x, y);
}
fn(); // undefined 1
fn(10); // 10 1
fn(10, 20); // 10 20

2)函数的非尾参数有默认值-该参数不能省略

function fn(x = 1, y) {
    console.log(x, y);
}
fn(10); // 10 undefined
fn(10, 20); // 10 20
fn(, 20); // 报错:Uncaught SyntaxError: Unexpected token ,
function fn(x, y = 1, z) {
    console.log(x, y, z);
}
fn(); // undefined 1 undefined
fn(1, 10); // 1 10 undefined
fn(10, , 20); // 报错:Uncaught SyntaxError: Unexpected token ,
fn(10, undefined, 20); // 10 1 20

1.5 函数的length属性

函数的length属性主要记录函数的参数个数,其中不包含有默认值的参数。

function fn1(x, y) {}
function fn2(x, y = 1) {}
console.log(fn1.length); // 2
console.log(fn2.length); // 1

1.6 函数参数的作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

let x = 1;
function fn(x, y = x) {
  console.log(x, y);
}
fn(2); // 2 2

在函数初始化时,参数形成一个作用域,参数变量y使用的是第一个参数x进行赋值,而不是全局变量x进行赋值。

let x = 1;
function fn(y = x) {
  console.log(y);
}
fn(); // 1

参数作用域没有x,则从全局变量中找x变量进行赋值。如果全局变量x不存则会报错。

1.7 应用

1)不可省略参数

function missingErr() {
    throw new Error('missing parameter');
}
function fn(arg1 = missingErr()) {
    console.log(arg1);
}
fn(123); // 123
fn(); // error: missing parameter

2) 可省略参数

function fn(arg1 = undefined) {
    console.log(arg1);
}
fn();

2 rest参数

用来获取函数的剩余参数,是一个数组,可以用来替代arguments。

语法:...变量名

function fn(x, ...rest) {
  let b = 0;
  for(var i = 0; i < rest.length; i++) {
    b = b + rest[i];
  }
  console.log(x, b);
}
fn('剩余变量:', 1, 2, 3, 4); // 剩余变量:10

剩余参数变量和arguments的却别:

  • rest是真正的数组,而arguments是伪数组,不能调用某些数组的方法;
  • rest参数变量后面不能再有其他的参数变量,否则报错;
  • rest参数变量不包含在函数的length属性里;
// arguments变量的写法:使用数组的slice方法将arguments转为真正数组,才能调用数组的sort方法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest参数的写法:简洁
const sortNumbers = (...numbers) => numbers.sort();

3 箭头函数-Arrow Function

3.1 检测浏览器是否支持箭头函数

'use strict';
var test => console.log('您的浏览器支持箭头函数;');

3.2 函数形式

1)简略模式

只有一个表达式,没有{...}和return语句,默认返回表达式的结果。

var fn = x => x*x;
fn(2); // 4

2)多语句

不能省略{...},且必须有return语句。

var fn = flag => {
    if (flag) {
        return true;
    } else {
        return false;
    }
}

3)简略模式:返回一个对象

这种情况下应用圆括号包住,以区别函数体。

var fn = x => ({foo: x})
var fn = x => {foo: x} // 当成一条执行语句执行,没有返回

3.3 函数参数

// 1、无参数:用圆括号表示
var fn1 = () => {
    console.log('您的浏览器支持箭头函数!');
};

// 2、一个参数:可以省去圆括号
var fn2 = (x) => x*x;
// 或
var fn2 = x => x*x;

// 3、多个参数:必须用圆括号
var fn3 = (x, y) => x*y;

3.4 箭头函数和匿名函数的区别

箭头函数的this总是指向词法作用域,即指向外层调用对象。

var person = {
  birth: 1991,
  getAge: function() {
    var age = () => new Date().getFullYear() - this.birth;
    return age();
  }
}
console.log(person.getAge()); // 27

因此,call和apply调用箭头函数时,无法绑定this,传入的第一个参数会被忽略。

var person = {
  birth: 1991,
  getAge: function() {
    var age = () => new Date().getFullYear() - this.birth;
    // 使用call或apply改变this作用域
    return age.call({birth: 1990}, 1);
  }
}
console.log(person.getAge()); // 27

var arr = [32, 3, 10, 13];
arr.sort((x, y) => {
    return x - y;
});
console.log(arr) // 3, 10, 13, 32

3.5 注意事项

  • 箭头函数的this对象是定义时所在的对象,而不是使用时所在的对象;
  • 箭头函数不能当构造函数使用;
  • 箭头函数没有arguments对象,使用rest变量代替;
  • 箭头函数的this对象的指向是固定的;

参考

1、阮一峰-ES6-函数的扩展;
2、廖雪峰-箭头函数;