函数参数默认值
ES5
的时候给函数参数赋值默认值的时候经常的写法是:
y = y || "World";
但是y
赋值一个空字符串他也会变为默认值"World"
,所以有了下面的写法:
if (typeof y === 'undefined') {
y = 'World';
}
ES6
中加入了参数设置默认值的写法:
function log(x, y = 'World') {
console.log(x, y);
}
如果使用了默认值,存在下面的限制:
- 函数体内不能使用
let
或const
再次声明参数 - 函数不能存在同名参数
- 默认值如果是一个表达式,那么默认值每次都要重新计算的
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
- 默认值是可以和解构赋值一起使用的,但是结构赋值的默认值并不是函数参数的默认值
function foo({x, y = 5}) { // 这样的写法并不是参数的默认值
console.log(x, y);
}
function foo({x, y = 5} = {}) { // 这才是参数的默认值,这叫做双重默认值
console.log(x, y);
}
对于双重默认只来说,推荐使用在解构赋值中使用默认值,因为这样就算真的给参数传递了一个空对象,解构赋值中的默认值依然生效
例如:
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
这时参数传递一个空对象,前面的x
和y
依然是有值的
参数默认值的位置
推荐将参数的默认值的位置写在后面的参数,这样就能在传递参数的时候省略掉后面的参数,如果参数的默认值在前面或者在中间,都没有办法省略的,只能传递一个undefined
注意参数的默认值只有undefined
生效,null
不生效
函数的length属性
对于rest
参数和有默认值的参数都不会被计入到length
属性中,而且,默认值后面的参数也不会被计入到length
属性中
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
默认参数的作用域
带有默认值参数的函数在初始化的时候,参数区域会形成一个单独的作用域,初始化完成后,这个作用域就会消失,如果没有参数的默认值,也就不会形成这种作用域。
这种单独的作用域产生的结果如下:
var x = 1;
function f(x, y = x) { // 这里的y不会等于全局的x,而是参数内部的x
console.log(y);
}
f(2) // 2
什么时候会等于全局的x呢?
let x = 1;
function f(y = x) { // 因为参数中没有x,所以这个时候y就等于全局的x
let x = 2;
console.log(y);
}
f() // 1
应用
如果某一个参数一定不能省略,就可以用默认值来实现抛异常
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
如果说参数的默认值是undefined
,表明这个参数是可以省略的。
rest参数
这个东西和Java
里面的可变参数是一个意思的,基本上每种语言的可变参数都是一样的,ES6
中的可变参数,就是一个真实的数组,和之前的arguments
这样的类数组对象不一样,可变参数可以直接使用数组能使用的方法,可变参数必须放在参数的最后一个位置
函数的length
属性也不包含rest
参数
对于严格模式的限制
只要参数使用了默认值、解构赋值、或者扩展运算符,就不能显式指定严格模式,否则报错。
但是可以下面这样写,就可以绕开这个限制
- 设定全局性的严格模式
'use strict';
function doSomething(a, b = a) {
// code
}
- 把函数包在一个无参数的立即执行函数里面。
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
函数的name属性
ES6
将函数的name
写入标准
- 匿名函数赋值给一个变量,那么使用这个
变量.name
会返回变量的名字,例如将一个匿名函数赋值给f
变量,使用f.name
会返回f
, - 具名函数赋值给一个变量,使用
变量.name
会得到具名函数原来的名字,而不是变量名,例如将一个具名函数fun
赋值给f
变量,这个时候使用f.name
会得到fun
,而不是f
- 使用
Function
构造函数返回一个函数实例,name
属性的值为anonymous
bind
返回的函数,name
属性会加上bound
前缀,例如"bound foo"
或者"bound "
,注意这里是有空格的。
箭头函数(重点)
箭头函数在日常使用中的场景太多,再来记录一些它的使用注意事项
-
如果函数只有一个参数,圆括号可以省略,这就意味着,没有参数或者又多个参数,圆括号就不能省略
-
如果代码块只有一条语句,并且要作为返回值,可以省略大括号和
return
,这也就意味着,多条语句不能省略大括号和return
-
如果函数只返回一个对象,但是不想写
return
,由于对象是用大括号{}
括起来的,所以为了避免和函数代码块的大括号混淆,必须要在对象外面加上括号,不加括号会和预期的结果不一样。 -
如果箭头函数只有一条语句且不需要返回值,但是还不想写大括号,这个时候需要在语句前面加上
void
-
箭头函数内部是不能使用
arguments
对象的,如果要这样用,可是使用rest
参数代替 -
箭头函数是没有
this
对象的,也就自然而然无法通过call()
、apply()
、bind()
方法改变箭头函数的this
作用域,它的this
和它所处的环境有关,所以在回调函数中可以放心的使用this
了。这个this
不会变成回调函数运行时所处的环境。比如说下面的情况:// jQuery 的写法 // 这个时候回调中的this指向的是定义时的环境 // 而不会被调用回调时候的环境所影响 $('#button').on('click', () => {/* ... */});
-
箭头函数不能当作构造函数来使用,就是因为他没有
this
-
箭头函数不可以使用
yield
命令,因为没有办法让箭头函数声明成Generator
函数 -
箭头函数中不仅没有
this
,arguments
、super
、new.target
也是不存在的
不适合使用箭头函数的情况:
- 定义一个对象中的方法的时候
- 需要动态
this
的时候
其他扩展
ES2017
允许函数的最后一个参数有尾逗号(trailing comma
)
ES2019
对函数实例的toString()
方法做出了修改,要求返回和原来一模一样的原始代码
ES2019
允许省略catch
命令的参数
try {
// ...
} catch {
// ...
}