箭头函数

箭头函数

(param1, param2, ..., paramN) => expression

//相当于:(param1, param2, ..., paramN) =>{ return expression; }
// 当只有一个参数时,圆括号是可选的:
(singleParam) => { statements }
singleParam => { statements }
// 没有参数的函数应该写成一对圆括号
() => { statements }

箭头函数与常规函数行为的差异?

1.命名函数参数

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参数重复了,因此,它被映射到传递给函数调用的第三个参数的值,覆盖了第一个参数(非严格模式下),严格模式下会报错!

箭头函数如何处理重复的参数?

与常规函数不同,无论在严格模式还是非严格模式下,箭头函数都不允许重复参数,重复的参数将引发语法错误

2.函数重载

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参数,注意:

  • rest参数是一个实际的函数参数,而arguments对象是一个绑定到函数作用域的内部对象
  • 一个函数只能有一个rest参数,而且它必须位于最后一个参数
  • rest 参数与命名参数一起使用时,它不包含所有传入的参数。但是,当它是惟一的函数参数时,表示函数参数this
  • rest参数指向包含所有捕获函数参数的数组对象,而arguments对象指向包含所有函数参数的类数组对象

3.构造函数

常规函数

使用new关键字调用常规JS函数,该函数作为类构造函数用于创建新的实例对象。

当使用new关键字调用常规JS函数时,将调用函数内部[[Construct]]方法来创建一个新的实例对象并分配内存。之后,函数体将正常执行,并将this映射到新创建的实例对象。最后,函数隐式地返回this(新创建的实例对象),只是在函数定义中指定了一个不同的返回值。

此外,所有常规JS函数都有一个prototype属性。函数的prototype属性是一个对象,它包含函数创建的所有实例对象在用作构造函数时共享的属性和方法

箭头函数

箭头函数永远不能使用new关键字调用,因为它们没有[[Construct]]方法,因此,箭头函数也不存在prototype属性

4.this

JS函数的每次调用都与调用上下文相关联,这取决于函数是如何调用的,或者在哪里调用的,因此,函数内部this值依赖于函数在调用时的调用上下文。

一般情況下的總結

  • 使用new关键字调用:this一般默认指向由函数的内部[[Construct]]方法创建的新实例对象。除非在函数定义中显式指定了不同的返回值。
  • 不使用new关键字直接调用:在非严格模式下,this指向window对象(浏览器中)。在严格模式下,this值为undefined;因此,试图访问或设置此属性将引发错误。
  • 间接使用绑定对象调用:Function.prototype对象提供了三种方法,可以在调用函数时将函数绑定到任意对象,即:call(),apply()和bind()。使用这些方法调用函数时,this指向指定的绑定对象。
  • 作为对象方法调用:this指向调用函数(方法)的对象,无论该方法是被定义为对象的自己的属性还是从对象的原型链中解析。
  • 作为事件处理程序调用:对于用作DOM事件侦听器的常规函数,this指向触发事件的目标对象、DOM元素、document或window 。

看一个例子: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

总结

  • 无论在严格模式还是非严格模式下,箭头函数都不能具有重复的命名参数。
  • 箭头函数没有arguments绑定。但是,它们可以访问最接近的非箭头父函数的arguments对象。
  • 箭头函数永远不能用作构造函数,自然的不能使用new关键字调用它们,因此,对于箭头函数不存在prototype属性。
  • 在函数的整个生命周期中,箭头函数内部的值保持不变,并且总是与接近的非箭头父函数中的值绑定。

你可能感兴趣的:(前端,javascript,js,bind)