(param1, param2, ..., paramN) => expression
//相当于:(param1, param2, ..., paramN) =>{ return expression; }
// 当只有一个参数时,圆括号是可选的:
(singleParam) => { statements }
singleParam => { statements }
// 没有参数的函数应该写成一对圆括号
() => { statements }
function logParams(first, second, third) {
console.log(first, second, third);
}
logParams('Hello', 'World', '!!!'); // "Hello" "World" "!!!"
logParams({ o: 3 }, [1, 2, 3]); // {o: 3} [1, 2, 3]
function logParams(first, second, first) {
console.log(first, second);
}
logParams('Hello', 'World', '!!!'); // “!!!" "World"
logParams({ o: 3 }, [1, 2, 3]); // undefined [1, 2, 3]
對比上面兩個函數,我們發現第二個函數中first参数重复了,因此,它被映射到传递给函数调用的第三个参数的值,覆盖了第一个参数(非严格模式下),严格模式下会报错!
箭头函数如何处理重复的参数?
与常规函数不同,无论在严格模式还是非严格模式下,箭头函数都不允许重复参数,重复的参数将引发语法错误
function average() {
const length = arguments.length;
if (length == 0) return 0; // 将参数转换为数组
const numbers = Array.prototype.slice.call(arguments);
const sumReduceFn = function (a, b) {
return a + Number(b)
}; // 返回数组元素的总和除以数组的长度
return numbers.reduce(sumReduceFn, 0) / length;
}
这样函数可以用任意数量的参数调用
现在尝试使用箭头函数语法复制average()
函数
const average = () => {
const length = arguments.length;
if (length == 0) return0;
const numbers = Array.prototype.slice.call(arguments);
const sumReduceFn = function (a, b) {
return a + Number(b)
};
return numbers.reduce(sumReduceFn, 0) / length;
}
会抛出一个引用错误,arguments 未定义
与常规函数不同,arguments不存在于箭头函数中。但是,可以访问非箭头父函数的arguments对象。基于这种理解,可以将average()函数修改为一个常规函数,该函数将返回立即调用的嵌套箭头函数执行的结果,该嵌套箭头函数就能够访问父函数的arguments。
function average() {
return (() => {
const length = arguments.length;
if (length == 0) return0;
const numbers = Array.prototype.slice.call(arguments);
const sumReduceFn = function (a, b) { return a + Number(b) };
return numbers.reduce(sumReduceFn, 0) / length;
})();}
对于上面问题是否存在替代方法?
ES6rest参数
const average = (...args) => {
if (args.length == 0) return 0;
const sumReduceFn = function (a, b) {
return a + Number(b)
};
return args.reduce(sumReduceFn, 0) / args.length;
}
关于rest参数,注意:
常规函数
使用new关键字调用常规JS函数,该函数作为类构造函数用于创建新的实例对象。
当使用new关键字调用常规JS函数时,将调用函数内部[[Construct]]方法来创建一个新的实例对象并分配内存。之后,函数体将正常执行,并将this映射到新创建的实例对象。最后,函数隐式地返回this(新创建的实例对象),只是在函数定义中指定了一个不同的返回值。
此外,所有常规JS函数都有一个prototype属性。函数的prototype属性是一个对象,它包含函数创建的所有实例对象在用作构造函数时共享的属性和方法
箭头函数
箭头函数永远不能使用new关键字调用,因为它们没有[[Construct]]方法,因此,箭头函数也不存在prototype属性
JS函数的每次调用都与调用上下文相关联,这取决于函数是如何调用的,或者在哪里调用的,因此,函数内部this值依赖于函数在调用时的调用上下文。
一般情況下的總結
看一个例子:processFormData函数将用作按钮单击事件侦听器
function processFormData(evt) {
evt.preventDefault();
const form = this.closest('form');
const data = new FormData(form);
const { action: url, method } = form;
}
button.addEventListener('click', processFormData, false);
This指向触发单击事件的dom元素,即button元素
如果将普通函数更改为箭头函数:
const processFormData= (evt) =>{
evt.preventDefault();
const form = this.closest('form');
const data = new FormData(form);
const { action: url, method } = form;
}
button.addEventListener('click', processFormData, false);
此时将会报错,而this却不再指向触发单击事件的dom元素,而是指向window对象。
button.addEventListener('click', processFormData.bind(button), false);
箭头函数:
与常规函数不同,箭头函数没有this的绑定。this的值将解析为最接近的非箭头父函数或全局对象的值。
但是,这并不能解释为什么不能使用bind()将事件侦听器箭头函数绑定到button元素。这是由于内部箭头函数的this值保持不变,并且无论调用上下文如何,都不能在其整个生命周期中更改。
一个简单的计时器:
function Timer(seconds = 60) {
this.seconds = parseInt(seconds) || 60;
console.log(this.seconds);
this.interval = setInterval(function () {
console.log(--this.seconds);
if (this.seconds == 0) {
this.interval && clearInterval(this.interval);
}
}, 1000);}
const timer = new Timer(30);
运行结果:在控制台上一直打印NaN,而this指向全局window对象
解决上述问题:
可以使用bind()将setInterval()回调函数中的this值绑定到新创建的实例对象,或者用一个箭头函数替换setInterval()回调函数,这样回调函数内就可以使用最近的非箭头父函数的this
this.interval = setInterval(() => {
console.log(--this.seconds);
if (this.seconds == 0) {
this.interval && clearInterval(this.interval);
}
}
此处this指向了Timer的this