函数参数的默认值
基本用法
@H_502_4@在ES6之前,不能直接为函数的参数指定默认值,为了避免这个问题,通常需要先判断一下参数y
是否被赋值,如果没有,再等于默认值。
@H_502_4@ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x,y = 'World') { console.log(x,y); } log('Hello') // Hello World log('Hello','China') Hello China log('Hello','') Hello
let
或const
再次声明。
function foo(x = 5) { let x = 1; error const x = 2; error }
x
是默认声明的,在函数体中,不能用let
或const
再次声明,否则会报错。
与解构赋值默认值结合使用
@H_502_4@参数默认值可以与解构赋值的默认值,结合起来使用。function foo({x,y = 5}) { console.log(x,y); } foo({}) undefined,5 foo({x: 1}) 1,5 foo({x: 1,y: 2}) TypeError: Cannot read property 'x' of undefined
foo
的参数是一个对象时,变量x
和y
才会通过解构赋值而生成。如果函数foo
调用时参数不是对象,变量x
和y
就不会生成,从而报错。如果参数对象没有y
属性,y
的默认值5才会生效。
参数默认值的位置
@H_502_4@通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。例一 function f(x = 1,y) { return [x,y]; } f() [1,undefined] f(2) [2,undefined]) f(,1) 报错 f(undefined,1] 例二 function f(x,z) { [undefined,5,undefined] f(1) 报错 f(1,undefined,2]
undefined
。
@H_502_4@如果传入undefined
,将触发该参数等于默认值,null
则没有这个效果。
function foo(x = 5,y = 6null) 5 null
x
参数对应undefined
,结果触发了默认值,y
参数等于null
,就没有触发默认值。
函数的length属性
@H_502_4@指定了默认值以后,函数的length
属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length
属性将失真。
(function (a) {}).length 1 (function (a = 5) {}).length 0 (function (a,b,c = 5) {}).length 2
length
属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了3个参数,其中有一个参数c
指定了默认值,因此length
属性等于3
减去1
,最后得到2
。
@H_502_4@这是因为length
属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,rest参数也不会计入length
属性。
(function(...args) {}).length 0
length
属性也不再计入后面的参数了。
(function (a = 0,c) {}).length 1
作用域
@H_502_4@一个需要注意的地方是,如果参数默认值是一个变量,则该变量所处的作用域,与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。var x = 1; x) { console.log(y); } f(2) 2
y
的默认值等于x
。调用时,由于函数作用域内部的变量x
已经生成,所以y
等于参数x
,而不是全局变量x
。
@H_502_4@如果调用时,函数作用域内部的变量x
没有生成,结果就会不一样。
let x = 1function f(y = x) { let x = 2; console.log(y); } f() 1
y
的默认值变量x
尚未在函数内部生成,所以x
指向全局变量,结果又不一样。
@H_502_4@利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
function throwIfMissing() { throw new Error('Missing parameter'); } function foo(mustBeProvided = throwIfMissing()) { mustBeProvided; } foo() Error: Missing parameter
foo
函数,如果调用的时候没有参数,就会调用默认值throwIfMissing
函数,从而抛出一个错误。
@H_502_4@从上面代码还可以看到,参数mustBeProvided
的默认值等于throwIfMissing
函数的运行结果(即函数名之后有一对圆括号),这表明参数的默认值不是在定义时执行,而是在运行时执行(即如果参数已经赋值,默认值中的函数就不会运行),这与python语言不一样。
@H_502_4@另外,可以将参数默认值设为undefined
,表明这个参数是可以省略的。
function foo(optional = undefined) { ··· }
rest参数
@H_502_4@ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。add(...values) { let sum = 0; for (var val of values) { sum += val; } sum; } add(2,3) 10
arguments变量的写法 sortNumbers() { Array.prototype.slice.call(arguments).sort(); } rest参数的写法 const sortNumbers = (...numbers) => numbers.sort();
报错 f(a,...b,c) { ... }
(function(a) {}).length function(...a) {}).length function(a,...b) {}).length 1
扩展运算符
含义
@H_502_4@扩展运算符(spread)是三个点(...
)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1,2,3]) 1 2 3 console.log(1,...[2,3,4],5 1 2 3 4 5 [...document.querySelectorAll('div')] [<div>,<div>,<div>]
替代数组的apply方法
@H_502_4@由于扩展运算符可以展开数组,所以不再需要apply
方法,将数组转为函数的参数了。
ES5的写法 f(x,1)"> ... } var args = [0,1,2]; f.apply( ES6的写法 ]; f(...args);
扩展运算符的应用
@H_502_4@(1)合并数组ES5 [1,1)">].concat(more) ES6 [1,...more]
ES5 a = list[0],rest = list.slice(1 ES6 [a,...rest] = list
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
@H_502_4@(3)函数的返回值 @H_502_4@JavaScript的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。var dateFields = readDateFields(database); var d = new Date(...dateFields);
Date
。
@H_502_4@4)字符串
@H_502_4@扩展运算符还可以将字符串转为真正的数组。
[...'hello'] [ "h","e","l","o" ]
'x\uD83D\uDE80y'.length 4 [...'x\uD83D\uDE80y'].length 3
var nodeList = document.querySelectorAll('div'); var array = [...nodeList];
querySelectorAll
方法返回的是一个nodeList
对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList
对象实现了Iterator接口。
name属性
@H_502_4@函数的name
属性,返回该函数的函数名。
foo() {} foo.name "foo"
name
属性,会返回空字符串,而ES6的name
属性会返回实际的函数名。
var func1 = () {}; ES5 func1.name "" ES6 func1.name "func1"
func1
等于一个匿名函数,ES5和ES6的name
属性返回的值不一样。
@H_502_4@bind
返回的函数,name
属性值会加上“bound ”前缀。
foo() {}; foo.bind({}).name "bound foo" (function(){}).bind({}).name "bound "