前言

本章介绍函数的扩展。有些不常用的知识了解即可。

本章原文链接:函数的扩展

函数参数的默认值

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

当函数形参没有被赋值时,才会将默认值赋值给函数参数。

// 默认值直接写在行参后面
function sampleFn(sample = 0, sample1 = 0) {
return sample + sample1;
}

注意:

  • 参数变量是默认声明的,所以不能用letconst再次声明。
  • 使用参数默认值时,函数不能有同名参数。
  • 参数默认值是惰性求值的。
  • 函数的默认值指定后,函数length属性返回的是没有指定默认值的参数的个数。
  • 参数的默认值一旦设定,函数进行声明初始化时,参数会形成一个单独的作用域(context)。
// 默认值直接写在行参后面
function sampleFn(sample = 0, sample1 = 0,sample = 1) { // 不能有同名参数 let sample = 1; // 不能再次声明 return sample + sample1;
}

注意:通常情况下,定义了默认值的参数,应该是函数的尾参数。也就是放在最后面。

解构赋值默认值

// 函数的默认值与结构赋值的默认值可以结合使用
function sampleFn({ sample = 0, sample1 = 0 } = {}) { // 函数参数默认值
return sample + sample1;
} console.log(sampleFn({ sample: 23, sample1: 33 })); // 56 参数需对应解构赋值的类型

作用域

当函数参数设置了默认值,函数进行声明初始化时,函数参数会生成一个单独的作用域,等到初始化结束,该作用域就会消失。而且该行为只在函数参数指定了默认值才会出现。

let sample = 1;

/*
在声明的时候出现单独作用域
在这个作用域中,变量没有定义,于是指向外层变量
函数调用时,函数内部变量影响不到默认值变量
*/
function sampleFn(sample1 = sample) {
let sample = 2;
console.log(sample1);
return sample1;
} sampleFn() // 1

rest 参数

ES6 引入 **rest 参数 **,用于获取函数的多余参数。

arguments 对象是类数组,rest 参数是真正的数组。



形式为:...变量名,函数的最后一个命名参数以...为前缀。

// 下面例子中 ...values 为 rest参数 ,用于获取多余参数
const sample = function (title, ...values) {
let sample = values.filter(
(item) => {
return item % 2 === 0;
}
) return (title + sample);
} console.log(sample("求偶数", 1, 2, 6, 2, 1)); // 求偶数 2, 6, 2

注意:rest参数 只能是函数的最后一个参数,函数的length不包括rest参数

严格模式

在JavaScript中,只要在函数中的严格模式,会作用于函数参数和函数体。

ES2016 规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

function sample() { // 参数使用默认值、解构赋值、扩展运算符
'use strict'; // 开启严格模式
}

name 属性

函数的name属性,返回该函数的函数名。

function sample() { };
let sample1 = function () { };
function sample2() {}; console.log(sample.name); // sample
console.log(sample1.name); // sample1 // bound sample2 使用了 bind 方法,输出会有bound前缀
console.log(sample2.bind({}).name);
console.log((new Function).name); // anonymous 构造函数的name值为 anonymous

箭头函数

简单介绍

ES 6 新增一种函数定义方法,使用箭头连接参数列与函数题。

箭头函数相当于匿名函数,并且简化了函数定义,箭头函数没有prototype

// 普通函数
let sample = function (item) {
return item;
}; // 上面函数等同于下面函数 // 使用箭头函数
let sample = (item) => { return item}; // 箭头函数

箭头函数简写

没错,箭头函数还可以简写

  1. 当参数只有一个时,可以省略箭头左边的括号,但没有参数时,括号不可以省略。
  2. 当函数体只有一个表达式时,可省略箭头右边的大括号,但同时必须省略return语句 并写在一行。
  3. 当函数体分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
// 下面几种函数写法都相同
let sample = function (item) {
return item;
}; let sample = (item) => { return item}; // 箭头函数 不省略 let sample = item => { return item}; // 省略左边圆括号 let sample = (item) => item; // 省略右边大括号和 return let sample = item => item; // 省略左边圆括号和右边花括号和return // 如果不需要返回值的特殊情况
let sample = item => void item;
console.log(sample()); // undefined

注意点

  • 箭头函数 的 This 默认指向定义它的作用域的 This
  • 箭头函数 不能用作构造函数。
  • 箭头函数 不可以使用 arguments 对象,该对象在函数体内不存在。
  • 箭头函数 不可以使用yield命令,也就不能作为 Generator 函数。

箭头函数的this

箭头函数 会继承自己定义时所处的作用域链上一层的this

箭头函数 this在定义的时候已经确定了,所以箭头函数this不会改变。

使用 call()apply() 方法时,也不能重新给箭头函数绑定thisbing()方法无效。

window.sample = "window 内 ";

function sampleFn() {
let thiz = this;
let sample = "sampleFn 内 "; let sampleObj = {
sample: "sampleObj 内 ", // 普通函数
sampleFn1: function () {
console.log(thiz === this);
console.log(this.sample);
}, // 箭头函数
sampleFn2: () => {
// 箭头函数的作用域为 sampleObj 上一层为 sampleFn
console.log(thiz === this); //箭头函数的 this
console.log(this.sample);
}
} sampleObj.sampleFn1(); // false, sampleObj 内
sampleObj.sampleFn2(); // true, window 内
} sampleFn();

尾调用优化

有两个概念

  1. 尾调用

    尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是调用另一个函数。

  2. 尾递归

    函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

ES6 明确规定,所有 ECMAScript 的实现,都必须部署“尾调用优化”。

这就是说,ES6 中只要使用尾递归,就不会发生栈溢出(或者层层递归造成的超时),相对节省内存。

这是什么意思呢?

尾调用的作用,在原文中是这样写的:

我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

换种方式解释吧

函数被调用的时候会有函数执行上下文被压入执行栈中,直到函数执行结束,对应的执行上下文才会出栈。

在函数A的内部调用函数B,如果函数B中有对函数A中变量的引用,那么函数A即使执行结束对应的执行上下文也无法出栈,如果函数B内部还有调用函数C那么要等函数C执行完,函数A、B对应的执行上下文才能出栈,以此类推,执行栈中要上一个函数(内层函数)的执行上下文,这就是尾调用优化。

// 尾递归
function sampleFn(sample) {
if (sample <= 1) return 1;
return sampleFn(sample - 1) + sample;
}
sampleFn(2);

注意 :

  • 当内层函数没有用到外层函数的内部变量的时候才可以进行尾调用优化。
  • 目前只有 Safari 浏览器支持尾调用优化,Chrome 和 Firefox 都不支持。

ES 6 的小修改

函数参数尾逗号

ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。

这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。

function sampleFn(
sample1,
sample2,
sample3, // 可以在最后一个参数后面加 逗号 ','
) {}

toString()修改

Function.prototype.toString()

ES2019 对函数实例的toString()方法做出了修改。明确要求返回一模一样的原始代码。

toString()方法返回函数代码本身,ES6前会省略注释和空格。

function sampleFn() {
// 注释
} let sample = sampleFn.toString();
console.log(sample);
//输出 完全一样的原始代码,包括空格与注释
/*
function sampleFn() {
// 注释
}
*/

catch 修改

ES2019 改变了catch语句后面必须携带参数的要求。允许catch语句省略参数。

try {
// ...
} catch { // 不带参数
// ...
}

ES6学习 第七章 函数的扩展的更多相关文章

  1. ES6学习 第五章 正则的扩展

    前言 本章介绍正则的扩展.有些不常用的知识了解即可. 本章原文链接:正则的扩展 RegExp 构造函数 从 ES6 开始,如果RegExp构造函数第一个参数是一个正则对象,并且第二个标志存在且为标志参 ...

  2. ES6学习笔记<三> 生成器函数与yield

    为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...

  3. Go语言学习笔记七: 函数

    Go语言学习笔记七: 函数 Go语言有函数还有方法,神奇不.这有点像python了. 函数定义 func function_name( [parameter list] ) [return_types ...

  4. C#高级编程 (第六版) 学习 第七章:委托和事件

    第七章 委托和事件 回调(callback)函数是Windows编程的一个重要方面,实际上是方法调用的指针,也称为函数指针. .Net以委托的形式实现了函数指针的概念,.Net的委托是类型安全的. 委 ...

  5. Java基础知识二次学习--第七章 容器

    第七章 容器   时间:2017年4月27日15:08:30 章节:07章01节~07章04节 视频长度:20:21 +12:38 +3:55 +2:57 内容:容器API 心得: Java API ...

  6. 流畅的python第七章函数装饰器和闭包学习记录

    本章讨论的话题 python如何计算装饰器句法 python如何判断变量是不是局部的(通过函数内部是否给变量赋值过来判断是否是局部变量) 闭包存在的原因和工作原理(闭包是一种函数,它会保留定义函数时存 ...

  7. ES6新特性3:函数的扩展

    本文摘自ECMAScript6入门,转载请注明出处. 一.函数参数默认值 1. ES6允许为函数的参数设置默认值,即直接写在参数定义的后面. function log(x, y = 'World') ...

  8. es6学习笔记10--箭头函数

    基本用法 ES6允许使用“箭头”(=>)定义函数. var f = v => v; 上面的箭头函数等同于: var f = function(v) { return v; }; 如果箭头函 ...

  9. ES6学习笔记七:生成器与异步操作

    一:Generator Generator 函数是一个普通函数,但是有两个特征.一是,function关键字与函数名之间有一个星号:二是,函数体内部使用yield表达式,输出不同的内部状态. 执行 G ...

  10. ES6系列_7之箭头函数和扩展

    1.默认值 在ES6中给我们增加了默认值的操作相关代码如下: function add(a,b=1){ return a+b; } console.log(add(1)); 可以看到现在只需要传递一个 ...

随机推荐

  1. 基于Html+腾讯云播SDK开发的m3u8播放器

    周末业余时间在家无事,学习了一下腾讯的云播放sdk,并制作了一个小demo(m3u8播放器),该在线工具是基于腾讯的云播sdk开发的,云播sdk非常牛,可以支持多种播放格式. 预览地址 m3u8pla ...

  2. Spring——静态/动态代理模式

    代理模式 代理模式: 静态代理 动态代理 学习aop之前,要先了解代理模式 静态代理 抽象角色:一般使用接口或者抽象类来实现 真实角色:被代理的角色 代理角色:代理真实角色:代理真实角色后,一般会做一 ...

  3. Go-测试-testing

  4. PG数据库存储验证

    PG数据库存储验证 背景 最近学习了SQLServer数据库的varchar和nvarchar的存储 想到PG数据库其实没让选择字符集,也没有nvarchar 所以想学习一下nvarchar的使用情况 ...

  5. ingress nginx 支持的K8S版本以及nginx版本信息

  6. [转帖]如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证

    https://www.jianshu.com/p/e1121d4d7084 引言 由于 HTTP 协议是无状态的,完成操作关闭浏览器后,客户端和服务端的连接就断开了,所以我们必须要有一种机制来保证客 ...

  7. [转帖]ORACLE等待事件:enq: TX - row lock contention

    https://www.cnblogs.com/kerrycode/p/5887150.html enq: TX - row lock contention等待事件,这个是数据库里面一个比较常见的等待 ...

  8. [转帖]Linux—vi/vim全局替换

    https://www.jianshu.com/p/4daa5dbc7dd5 vim全局替换   在linux系统中编辑文件或者配置时,常常会用到全局替换功能. 语法格式 :%s/oldWords/n ...

  9. [转帖]发布即巅峰!万字长文:Java性能调优六大工具:MAT内存分析工具

    jianshu.com/p/4ed3dd8b7b83 MAT是MemoryAnalyzerTool的简称,它是一款功能强大的Java堆内存分析器,可以用于查找内存泄漏以及查看内存消耗情况.MAT是 基 ...

  10. 利用pearcmd.php本地文件包含(LFI)

    本文主要是为了学习如何用pearcmd进行本地文件包含 0x00  环境准备 首先先在docker中安装一个php环境. docker exec -it [container id] /bin/bas ...