这篇文章主要介绍了函数的扩展,函数的扩展只要有三个方面:
- 参数的默认值
- 箭头函数
- 关于尾调用的优化
1:参数的默认值(会形成一个单独的作用域)
可以在直接定义函数的时候,定义好参数如果没传或者传错 undefined。
//解构的基本用法
//用法1:
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello');
//Hello World
log('Hello', 'China');
/* Hello China */
log('Hello', '');
/* Hello */
参数的默认值只有在没传时才会生效。
//用法2:
function week({ x, y = 5 }) {
console.log(x, y);
}
week({});
/* undefined 5 */
week({x: 1});
/* 1 5 */
week({x: 1, y: 2});
/* 1 2 */
week();
/* TypeError: Cannot read property 'x' of undefined */
//用法3:
function week({x, y = 5} = {}) {
console.log(x, y);
}
week();
/* undefined 5 */
解构和参数的默认值需要注意的点:
- 参数的变量已经默认声明,不能用let或const再次声明
function week(x = 5) { let x = 1; const x = 2; } /* SyntaxError: Identifier 'x' has already been declared */
- 函数不能有同名参数,因为参数的变量已经默认声明所以不能再次声明
function week(x, x, y = 1) { /* ... */ } /* SyntaxError: Duplicate parameter name not allowed in this context */
- 参数默认值是惰性求值
当我们真正运算走这个函数的时候,它才会去处理默认的参数let x = 99; function week(p = x + 1) { console.log(p); } week(); /* 100 */ x = 100; week(); /* 101 */
- 参数默认值一般用于尾部,比如一个函数
- length属性,也就是函数的name和let,返回没有指定默认值的参数个数
(function (a) {}).length; //1 (function (a = 5) {}).length; //0 (function (a, b, c = 5) {}).length; //2 (function (a = 0, b, c) {}).length; //0 (function (a, b = 1, c) {}).length; //1 (function(...args) {}).length; //0
- 设置了参数的默认值,参数会形成一个单独的作用域
var x = 1; function f(x, y = x) { console.log(y); } f(2); //2 let x = 1; function f(y = x) { let x = 2; console.log(y); } f(); //1
2:rest参数,只能有一个参数,部分场景可以替代argument、类似于argument。
比如一个函数的argument我们知道这是函数的参数,argument实际上是官方不推荐的一种用法。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3);
/* 10 */
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
const sortNumbers = (...numbers) => numbers.sort();
3:严格模式,在严格模式下,使用了默认值、解构赋值或者扩展不能使用严格模式。
4:name 返回函数名
function week() {}
week.name;
// "week" *
var f = function () {};
f.name // ""
// ES6
f.name // "f"
5:箭头函数
箭头函数主要的两点是:第一点它的this指向是在它当时定义所在的作用域,第二个是它没有一个作用域的提升。
// 单个参数
var f = v => v;
var f = function (v) {
return v;
};
// 多个参数
var sum = (num1, num2) => num1 + num2;
var sum = function(num1, num2) {
return num1 + num2;
};
// return,有两种场景,箭头函数里面直接写return
// 或者不写的话箭头函数就会默认把这个结果当成一个return。
// 也就是说当我们返回一个对象时,没有return语句的时候我们需要再对象外面再包一个括号
var sum = (num1, num2) => { return num1 + num2; }
// 1 返回对象
let getTempItem = id => { id: id, name: "Temp" };
// 2 Unexpected token :
let getTempItem = id => ({ id: id, name: "Temp" });
// 结合解构
const full = ({ first, last }) => first + ' ' + last;
// rest
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5);
// [1,2,3,4,5]
箭头函数使用的注意点:
- 函数体内的this对象指向定义时所在的对象而不是使用时所在的对象
function week() { setTimeout(() => { console.log('id:', this.id); }, 100); } //定义全局id(使用时的所在id) var id = 21; //调用,改变了this的值 week.call({ id: 42 }); // 42 function week() { return () => { return () => { return () => { console.log('id:', this.id); }; }; }; } //定义时的id=1,决定了它的作用域,因此下面的t1,t2,t3的输出结果均为1 var f = week.call({id: 1}); var t1 = f.call({id: 2})()(); var t2 = f().call({id: 3})(); var t3 = f()().call({id: 4}); // 1 // 对象不构成作用域 const cat = { lives: 9, jumps: () => { this.lives--; } }
- 不可以当作构造函数,因为定义的时候是全局的
- 不可以使用argument对象,这是一个规定,需要使用rest来代替
- 不可以使用yield命令
6:尾调用
一般用于严格模式,也就是说在严格模式下面,ES6做了一个尾调用的优化;但是在非严格模式下虽然也可以使用尾调用,但是没有优化。
尾调用基本概念:最后一步是调用另一个函数,不适用于外部变量时只保留内层函数的调用帧(外部的变量不应该保存在return这个函数里面,也就是返回的return并不使用这个作用域)。
function f(x){
return g(x);
}
7:尾递归(会有一个调用栈的堆积)
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5);
// 尾递归
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1)
8:函数参数的尾逗号,方便代码版本管理
func(
'week',
'month',
);
网友评论