【05】ES6:函数的扩展

一、函数参数的默认值

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

1、基本用法

默认值的生效条件

不传参数,或者明确的传递 undefined 作为参数,只有这两种情况下,默认值才会生效。

注意:null 就是 null,不会使用默认值。

javascript 复制代码
// ES6 之前的默认值实现方式
const log = (x, y) => {
	// typeof类型检测:返回表示当前数据类型的字符串
    if (typeof y === 'undefined') {
        y = 'world'
    }
    
    console.log(x, y)
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
javascript 复制代码
// ES6 默认值实现方式
function log(x, y = 'World') {
	console.log(x, y)
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

默认值的一些规则

参数变量是默认声明的,所以不能用let或const再次声明。

javascript 复制代码
function foo(x = 5) {
	let x = 1 // error
	const x = 2 // error
}

使用参数默认值时,函数不能有同名参数。

javascript 复制代码
// 不报错
function foo(x, x, y) {
	// ...
}

// 报错 SyntaxError: Duplicate parameter name not allowed in this context
function foo(x, x, y = 1) {
	// ...
}

参数默认值是惰性求值的。参数默认值不是传值的,而是每次都重新计算默认值表达式的值。

javascript 复制代码
let x = 99
function foo(p = x + 1) {
	console.log(p)
}

foo() // 100

x = 100
foo() // 101

2、与解构赋值默认值结合使用

函数参数默认值 可以与 解构赋值的默认值,结合起来使用。通过给函数参数设置默认值,可以避免在没有提供参数时出现错误。

javascript 复制代码
// 只使用对象的解构赋值默认值
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()调用时没提供参数,变量 x 和 y 就不会生成,从而报错
foo() // TypeError: Cannot read property 'x' of undefined


// -------------------------------------------


// 使用对象的解构赋值默认值 + 函数参数的默认值
function foo({ x, y = 5 } = {}) {
	console.log(x, y)
}

foo() // undefined 5
javascript 复制代码
// 只使用对象的解构赋值默认值
function fetch(url, { body = '', method = 'GET', headers = {} }) {
	console.log(method)
}

fetch('http://example.com', {}) // 'GET'
fetch('http://example.com') // 报错

// --------------------------------------------------

// 使用对象的解构赋值默认值 + 函数参数的默认值
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
	console.log(method)
}

fetch('http://example.com') // 'GET'

注意,函数参数的默认值生效以后,参数解构赋值依然会进行。

javascript 复制代码
// 参数默认值 { a: 'hello' } 生效;进行解构赋值,从而触发参数变量 b 的默认值生效。
function f({ a, b = 'world' } = { a: 'hello' }) {
	console.log(b)
}

f() // world


// 解构赋值的默认值只在属性值为 undefined 时才会生效
function f({ a, b = 'world' } = { b: 'hello' }) {
	console.log(b)
}

f() // hello

3、参数默认值的位置

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

javascript 复制代码
// 例一
function f(x = 1, y) {
	return [x, y]
}

f() // [1, undefined]
f(2) // [2, undefined]
f(, 1) // 报错
f(undefined, 1) // [1, 1]

// 例二
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。

如果传入 undefined,将触发该参数等于默认值,null 则没有这个效果。

javascript 复制代码
function foo(x = 5, y = 6) {
	console.log(x, y)
}

foo(undefined, null) // 5 null

4、函数的 length 属性

函数的 length 属性,等于该函数预期传入的参数个数。

当函数指定默认值后,length 属性将失真。将返回没有指定默认值的参数个数,如果设置了默认值的参数不是尾参数,那么 length 属性也不再计入后面的参数了。

javascript 复制代码
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

(function(...args) {}).length // 0

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

5、应用

利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

javascript 复制代码
function throwIfMissing() {
	throw new Error('Missing parameter')
}

function foo(mustBeProvided = throwIfMissing()) {
	return mustBeProvided
}

foo() // Error: Missing parameter

上面代码的 foo 函数,如果调用的时候没有参数,就会调用默认值 throwIfMissing 函数,从而抛出一个错误。

从上面代码还可以看到,参数 mustBeProvided 的默认值等于 throwIfMissing 函数的运行结果(注意函数名 throwIfMissing 之后有一对圆括号),这表明参数的默认值不是在定义时执行,而是在运行时执行。如果参数已经赋值,默认值中的函数就不会运行。

另外,可以将参数默认值设为 undefined,表明这个参数是可以省略的。

javascript 复制代码
function foo(optional = undefined) { ··· }

二、reset 参数(...不定参数)

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

javascript 复制代码
function add(...values) {
	let sum = 0
	
	for (var val of values) {
		sum += val
	}
	
	return sum
}

add(2, 5, 3) // 10
javascript 复制代码
// arguments 变量的写法
function sortNumbers() {
	// arguments 为类数组对象,需先使用 Array.from 转换为数组
	return Array.from(arguments).sort()
}

// rest 参数的写法 (reset 参数为真正的数组)
const sortNumbers = (...numbers) => numbers.sort()

注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

javascript 复制代码
// 报错
function f(a, ...b, c) {
	// ...
}

函数的 length 属性,不包括 rest 参数。

javascript 复制代码
(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

三、箭头函数

ES6 规定了可以使用 "箭头" => 来定义一个函数,语法更加简洁。它没有自己的 this、arguments、super 或 new.target,箭头函数表达式更适用于那些本来需要匿名函数的地方,但它不能用作构造函数。

1、基本使用

普通函数

javascript 复制代码
function 函数名() {}

const 变量名 = function () {}

箭头函数

javascript 复制代码
(参数) => {函数体}

const 变量名 = () => {}
javascript 复制代码
// 基本语法
const add = (x, y) => {
	return x + y
}

// 有且只有一个参数,()可以省略
const add = x => {
	return x + 1
}

// 有且只有一条语句,且为 returen 语句,{} 和 return 可以省略
const add = (x, y) => x + y

// return 为对象时,对象外需要加 ()
const add = (x, y) => {
    return {
        value: x + y
    }
}
const add = (x, y) => ({ value: x + y })

2、函数 this 指向

es5 中的 this 指向

函数 this 的取值,是在函数执行的过程中确定的,不是在函数定义时确定的。

(1) 作为普通函数被调用,this 指向 window

(2) 作为对象方法被调用时,this 指向当前对象

(3) 在构造函数中(es5, es6的class方法),this 指向通过构造函数创建的实例对象

(5) 定时器中函数的 this 指向 window,定时器中函数相当于普通函数被调用(setTimeout | setInterval)

es6 中箭头函数的 this 指向

箭头函数中的 this 是在定义的时候绑定的,this 取上级作用域的 this,箭头函数本身不会决定 this 的值。

call、 apply 、 bind

fn.call(this, ...params) 和 fn.apply(this, [params]) 都是用来改变函数的this指向

区别是传参不同,call()接受的是列表,apply()接受的是数组

fn.bind(this, params) 方法也是用来改变函数this指向,但是不会立即执行,而是返回一个新函数

3、不适用箭头函数的场景

作为构造函数

因为箭头函数没有 this,而构造函数的核心就是 this。

需要 this 指向调用对象的时候

因为箭头函数没有 this,所以如果箭头函数中出现了 this,那么这个 this 就是外层的!

给事件绑定方法时,比如说通过 addEventListener 给某个事件绑定方法,如果使用箭头函数,此时 this,会指向父级的this window
在定义某个对象的方法时,不可以使用箭头函数
在 vue 的 methods 中的方法,也不可以使用箭头函数,会使 this 指向的不是当前的vm实例,发生错误

需要使用 arguments 的时候

箭头函数没有 arguments。(这个问题有替代解决方案:不定参数)

没有原型

由于箭头函数不能用作构造函数,它们也没有自己的原型。因此,不能使用 prototype 属性来添加新方法。

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

四、函数参数的尾逗号

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

此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。

javascript 复制代码
function clownsEverywhere(
	param1,
	param2
) { /* ... */ }

clownsEverywhere(
	'foo',
	'bar'
)

上面代码中,如果在 param2 或 bar 后面加一个逗号,就会报错。

如果像上面这样,将参数写成多行(即每个参数占据一行),以后修改代码的时候,想为函数 clownsEverywhere 添加第三个参数,或者调整参数的次序,就势必要在原来最后一个参数后面添加一个逗号。这对于版本管理系统来说,就会显示添加逗号的那一行也发生了变动。这看上去有点冗余,因此新的语法允许定义和调用时,尾部直接有一个逗号。

javascript 复制代码
function clownsEverywhere(
	param1,
	param2,
) { /* ... */ }

clownsEverywhere(
	'foo',
	'bar',
)

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

五、catch 命令的参数省略

JavaScript 语言的 try...catch 结构,以前明确要求 catch 命令后面必须跟参数,接受 try 代码块抛出的错误对象。

javascript 复制代码
try {
	// ...
} catch (err) {
	// 处理错误
}

上面代码中,catch命令后面带有参数err。

很多时候,catch代码块可能用不到这个参数。但是,为了保证语法正确,还是必须写。ES2019 做出了改变,允许catch语句省略参数。

javascript 复制代码
try {
	// ...
} catch {
	// ...
}
相关推荐
一 乐21 分钟前
智慧党建|党务学习|基于SprinBoot+vue的智慧党建学习平台(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习
BBB努力学习程序设计1 小时前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html
BBB努力学习程序设计1 小时前
CSS3渐变:用代码描绘色彩的流动之美
前端·html
冰暮流星1 小时前
css之动画
前端·css
jump6802 小时前
axios
前端
spionbo2 小时前
前端解构赋值避坑指南基础到高阶深度解析技巧
前端
用户4099322502122 小时前
Vue响应式声明的API差异、底层原理与常见陷阱你都搞懂了吗
前端·ai编程·trae
开发者小天2 小时前
React中的componentWillUnmount 使用
前端·javascript·vue.js·react.js
永远的个初学者3 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js