ES6函数扩展(箭头函数)

一、函数参数的用法
1、参数默认值的设置,与ES5 对比
ES5中设置参数默认值的写法

 function animal(name, type) {
  var name = name || 'yuan';
  var type = type || 'monkey';
  console.log(name, type);
}

这个写法有个缺陷:参数传递进来的布尔值必须为true,如果传入的值为空字符串或者undefined 等这些转换为false的参数,则会影响结构。所以,ES6为了弥补这个缺陷,做了如下设置:

function animal(name = 'yuan', type = 'monkey') {
  console.log(name, type);
}

2、传入参数类型对输出结果的影响

function animal(name = 'yuan', type = 'monkey') {
  console.log(name, type);
}
animal(); // yuan monkey
animal(name = 'dog');  // dog monkey
animal(name = 'yuan', undefined); // yuan monkey
animal(name = 'yuan', null); // yuan null
animal(false, undefined); // false "monkey"
animal(0, undefined); // 0 "monkey"

上面代码4中调取函数的方式,只有不传和传入undefined 的会触发默认值,而传入其它值或者null,则不会触发默认值。
3、注意参数放置位置

// 错误写法
function animal(name = 'yuan', type) {
  console.log(name, type);
}
// 正确写法
function animal(name, type = 'monkey') {
  console.log(name, type);
}

如上,在设置了默认值的参数后,就不需要在设置其他参数了,通常情况下,若有参数为默认值时,一般是放在尾参数位置。
4、参数默认值与解构赋值默认值的结合使用
对比下面两种写法

// 第一种写法,参数默认值为空对象,解构赋值有具体的默认值
function method1({x = 0, y = 0} = {}) {
  return [x, y];  
}
// 第二种写法,参数默认值是一个具体的属性对象,而对象解构赋值没有设置默认值 
function method2({x, y} = {x: 0, y: 0}) {
  return [x, y];
}
// 函数没有参数时
method1(); // [0, 0]
method2(); // [0, 0]

// x 和 y 都有值
method1({x: 2, y: 3}); // [2, 3]
method2({x: 2, y: 3}); // [2, 3]

// x 有值, y 没有值
method1({x: 2}); // [2, 0]
method2({x: 2}); // [2, undefined]

// x 没有值, y 有值
method1({y: 2}); // [0, 2]
method2({y: 2}); // [undefined, 2

// x 和 y 都没有值
method1({});  // [0, 0]
method2({}); // [undefined, undefined]

5、使用参数默认值对函数length 属性的影响

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function(a, ...b) {} ).length // 1

由上面代码可以看出,函数的参数在指定默认值之后,函数的length 属性会失真,返回的length 值,是没有指定默认值的参数的个数,注意这里的length 也不包括rest参数(如第二点介绍)的个数。
6、注意点
因函数参数是默认声明的,如果如果用let 或 const 重新声明变量,会报错:

function animal(name = "yuan") {
  let name = dog;
  console.log(name);
}
animal(); // 报错: Uncaught SyntaxError: Identifier 'name' has already been declared

二、rest 参数
案例 呈现

// 求和,把结果赋值给 result
function sum(result, ...values) {
  console.log(values); // [1, 2, 3, 4],这个变量返回的是一个数组
  values.forEach( function(value, index) {
    result += value;
  })
  console.log(result); 
}
sum(11, 1, 2, 3, 4,); // 21

如上,rest参数(3个点 + 变量名)表示的是:获取函数多余的参数,且这个变量是一个数组。还有一点要注意的是rest参数必须是尾参数,后面不能加其他的参数,否则会报错:

// 错误写法
function sum(res, ...values, another) {
  console.log(values);
}
sum(); // 报错:Uncaught SyntaxError: Rest parameter must be last formal parameter

三、name属性
ES6中增加了函数的name属性

const animal = function() {};
animal.name;  // "animal"

Function 构造函数会发的函数实例,name 属性的值为“anonymous”

(new Function).name; //  "anonymous"

bind 返回的函数,name属性值会加上“bound”前缀

function animal() {};
animal.bind({}).name; // "bound animal"

匿名函数的bind 返回的值“bound”

(function () {}).bind({}).name; //  "bound"

四、箭头函数
1、定义
用箭头“=>”来定义函数。
2、用法
对比

// ES5 写法
var sum = function(a) {
  return a;
}
// ES6 写法
var sum = a => a;

如上代码,在ES6中,第一个a表示函数参数,箭头“=>”后面的 a 表示函数体。
上述只是针对一个参数和函数体只有一条语句的写法,若函数参数的个数和函数体的语句超过1个要如何表示呢?如下:

var sum = (a, b) => { return a + b};
sum(1,4); // 5

如上,若函数的参数个数超过一个时,需要用圆括号“()”来代表参数,函数体的语句条数超过一条时,需要用大括号将它们括起来。
当函数体中返回的是对象时,我们需要将其用圆括号“()”括起来:

var person = name => ({ name: 'yuan', type: 'monkey'});

3、使用注意点
(1)、函数体内的 this 指向,指向的是定义时所在的对象,而不是使用时所在的对象。
在箭头函数中 this 指向是固定的:

function foo() {
 setTimeout( () => {
    console.log('id:', this.id)
  }, 100);
}
var id = 21;
foo.call({ id: 42}); // 42

如上输出结果,此处的this 指向并不是全局对象 window,因为在箭头函数中,this 总是指向函数定义生效时所在的对象,所以输出的结果是 42。
如果还不清楚箭头函数 this 指向的含义,我们可以这样通俗的理解:在JavaScript 中每一个 function 都有一个独立的运行上下文,而箭头函数不是一个普通的 function ,没有自己的运行上下文。所以在箭头函数中写的 this,具体指的是包含这个箭头函数最近的 function 的上下文中的 this,如果没有最近的 function,this 指向的是全局。
(2)、不能当做构造函数用,即不能使用 new 命令,否则会报错
(3),、不可以使用arguments 对象,该对象哎函数体内不存在。如果要用,使用 rest 参数代替
(4)、不能使用 yield 命令,因为箭头函数不能用作Generator 函数
六、函数的尾调用
1、定义
尾调用是函数式编程的一个重要概念,是指某个函数的最后一步调用另一个函数。

function f(x) {
  return g(x);
}

函数f 的最后一步是调用函数g,这就是尾调用。
2、尾递归
函数调用自身叫做递归,如果尾调用自身就是尾递归。

function factorial(n) {
  if(n === 1) return 1;
  return n * factorial(n - 1);
}
factorial(5); // 120

在ES6 中只要使用尾递归,就不会发生栈溢出,相对节省内存。
3、尾递归改写
为了确保最后一步只调用自身,需要对尾递归函数进行改写,把所有用到的内部变量改写成函数的参数。
使用柯里化函数编程思想,将多参数的函数转换成单参数的形式:

function curring(fn, n) {
  return function (m) {
    return fn.call(this, m, n);
  }
}

function tailFactorial(n, total) {
  if(n === 1) return total;
  return tailFactorial(n - 1, n * total);
}
const factorial = curring(tailFactorial, 1);
factorial(5); // 120

采用ES6的函数默认值改写:

function factorial(n, total = 1) {
  if(n === 1) return total;
  return factorial(n - 1, n * total);
}
factorial(5); // 120

在尾调用优化时,循环是可以用递归代替的,而一旦使用递归,就最好使用尾递归。
五、总结
1、本章需要掌握ES6中函数的表示方式,以及 rest 参数的使用。
2、本章重点:箭头函数的表示方法,以箭头函数使用的注意事项,特别是箭头函数中 this 的指向问题。

戳我博客

章节目录

1、ES6中啥是块级作用域?运用在哪些地方?
2、ES6中使用解构赋值能带给我们什么?
3、ES6字符串扩展增加了哪些?
4、ES6对正则做了哪些扩展?
5、ES6数值多了哪些扩展?
6、ES6函数扩展(箭头函数)
7、ES6 数组给我们带来哪些操作便利?
8、ES6 对象扩展
9、Symbol 数据类型在 ES6 中起什么作用?
10、Map 和 Set 两数据结构在ES6的作用
11、ES6 中的Proxy 和 Reflect 到底是什么鬼?
12、从 Promise 开始踏入异步操作之旅
13、ES6 迭代器(Iterator)和 for...of循环使用方法
14、ES6 异步进阶第二步:Generator 函数
15、JavaScript 异步操作进阶第三步:async 函数
16、ES6 构造函数语法糖:class 类

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

推荐阅读更多精彩内容

  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    呼呼哥阅读 3,265评论 0 1
  • 1.函数参数的默认值 (1).基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
    赵然228阅读 648评论 0 0
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,348评论 0 5
  • 参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。 使用参数默认值时,函...
    ROBIN2015阅读 292评论 0 0
  • Spring XML 配置 jsp页面 language_en_US.properties language_zh...
    不懂i_阅读 648评论 0 0