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 函数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    呼呼哥阅读 3,339评论 0 1
  • 1.函数参数的默认值 (1).基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
    赵然228阅读 676评论 0 0
  • [TOC] 参考阮一峰的ECMAScript 6 入门参考深入浅出ES6 let和const let和const都...
    郭子web阅读 1,767评论 0 1
  • 第 2 章:函数基础 函数式编程不是仅仅用 function 这个关键词来编程。如果真这么简单,那我这本书可以到此...
    iKcamp阅读 916评论 0 7
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,420评论 0 5