
2025-02-02 日报 Day85

今日的鸡汤
这个世界上,从来就没有人能替你成长,未来要靠自己去奋力争取。只有自己有伞,才能少被雨淋。
今日学习内容
1、JS 红皮书 P293-296 第十章:函数
今日笔记
1、默认参数值: 实现默认参数的一种常用方式就是检测某个参数是否等于 undefined,如果是则意味着没有传这个参数,那就给它赋一个值:
1 | function makeKing(name) { |
ECMAScript 6 之后就不用这么麻烦了,因为它支持显式定义默认参数了。下面就是与前面代码等价的 ES6 写法,只要在函数定义中的参数后面用=就可以为参数赋一个默认值:
1 | function makeKing(name = 'Henry') { |
给参数传 undefined 相当于没有传值,不过这样可以利用多个独立的默认值:
1 | function makeKing(name = 'Henry', numerals = 'VIII') { |
在使用默认参数时,arguments 对象的值不反映参数的默认值,只反映传给函数的参数。当然,跟 ES5 严格模式一样,修改命名参数也不会影响 arguments 对象,它始终以调用函数时传入的值为准:
1 | function makeKing(name = 'Henry') { |
默认参数值并不限于原始值或对象类型,也可以使用调用函数返回的值:
1 | let romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI']; |
箭头函数同样也可以这样使用默认参数,只不过在只有一个参数时,就必须使用括号而不能省略了:
1 | let makeKing = (name = 'Henry') => `King ${name}`; |
2、默认参数作用域与暂时性死区: 因为在求值默认参数时可以定义对象,也可以动态调用函数,所以函数参数肯定是在某个作用域中求值的。
给多个参数定义默认值实际上跟使用 let 关键字顺序声明变量一样。来看下面的例子:
1 | function makeKing(name = 'Henry', numerals = 'VIII') { |
因为参数是按顺序初始化的,所以后定义默认值的参数可以引用先定义的参数。看下面这个例子:
1 | function makeKing(name = 'Henry', numerals = name) { |
参数初始化顺序遵循“暂时性死区”规则,即前面定义的参数不能引用后面定义的。像这样就会抛出错误:
1 | // 调用时不传第一个参数会报错 |
参数也存在于自己的作用域中,它们不能引用函数体的作用域:
1 | function makeKing(name = 'Henry', numerals = defaultNumeral) { |
3、参数扩展与收集:
- 扩展参数: 在给函数传参时,有时候可能不需要传一个数组,而是要分别传入数组的元素。假设有如下函数定义,它会将所有传入的参数累加起来:这个函数希望将所有加数逐个传进来,然后通过迭代 arguments 对象来实现累加。如果不使用扩展操作符,想把定义在这个函数这面的数组拆分,那么就得求助于 apply()方法:
1
2
3
4
5
6
7
8let values = [1, 2, 3, 4];
function getSum() {
let sum = 0;
for (let i = 0; i < arguments.length; ++i) {
sum += arguments[i];
}
return sum;
}但在 ECMAScript 6 中,可以通过扩展操作符极为简洁地实现这种操作。对可迭代对象应用扩展操作符,并将其作为一个参数传入,可以将可迭代对象拆分,并将迭代返回的每个值单独传入。1
console.log(getSum.apply(null, values)); // 10
比如,使用扩展操作符可以将前面例子中的数组像这样直接传给函数:因为数组的长度已知,所以在使用扩展操作符传参的时候,并不妨碍在其前面或后面再传其他的值,包括使用扩展操作符传其他参数:1
console.log(getSum(...values)); // 10
4、收集参数: 在构思函数定义时,可以使用扩展操作符把不同长度的独立参数组合为一个数组。这有点类似arguments 对象的构造机制,只不过收集参数的结果会得到一个 Array 实例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26console.log(getSum(-1, ...values)); // 9
console.log(getSum(...values, 5)); // 15
console.log(getSum(-1, ...values, 5)); // 14
console.log(getSum(...values, ...[5,6,7])); // 28
let values = [1,2,3,4]
function countArguments() {
console.log(arguments.length);
}
countArguments(-1, ...values); // 5
countArguments(...values, 5); // 5
countArguments(-1, ...values, 5); // 6
countArguments(...values, ...[5,6,7]); // 7
function getProduct(a, b, c = 1) {
return a * b * c;
}
let getSum = (a, b, c = 0) => {
return a + b + c;
}
console.log(getProduct(...[1,2])); // 2
console.log(getProduct(...[1,2,3])); // 6
console.log(getProduct(...[1,2,3,4])); // 6
console.log(getSum(...[0,1])); // 1
console.log(getSum(...[0,1,2])); // 3
console.log(getSum(...[0,1,2,3])); // 3收集参数的前面如果还有命名参数,则只会收集其余的参数;如果没有则会得到空数组。因为收集参数的结果可变,所以只能把它作为最后一个参数:1
2
3
4
5
6function getSum(...values) {
// 顺序累加 values 中的所有值
// 初始值的总和为 0
return values.reduce((x, y) => x + y, 0);
}
console.log(getSum(1,2,3)); // 6箭头函数虽然不支持 arguments 对象,但支持收集参数的定义方式,因此也可以实现与使用arguments 一样的逻辑:1
2
3
4
5
6
7
8
9
10// 不可以
function getProduct(...values, lastValue) {}
// 可以
function ignoreFirst(firstValue, ...values) {
console.log(values);
}
ignoreFirst(); // []
ignoreFirst(1); // []
ignoreFirst(1,2); // [2]
ignoreFirst(1,2,3); // [2, 3]另外,使用收集参数并不影响 arguments 对象,它仍然反映调用时传给函数的参数:1
2
3
4let getSum = (...values) => {
return values.reduce((x, y) => x + y, 0);
}
console.log(getSum(1,2,3)); // 61
2
3
4
5
6function getSum(...values) {
console.log(arguments.length); // 3
console.log(arguments); // [1, 2, 3]
console.log(values); // [1, 2, 3]
}
console.log(getSum(1,2,3));