箭头函数与普通函数的区别:
箭头函数的this指向规则:
1. 箭头函数没有prototype
直接看个例子就能说明:
let a = () =>{};
console.log(a.prototype); // undefined
function b() {};
console.log(b.prototype); // {constructor: ƒ}
2.箭头函数的this指向在定义的时候的外层第一个普通函数的this。
同样的我们来看一个例子:
let a,
barObj = { msg: 'bar的this指向' };
fooObj = { msg: 'foo的this指向' };
function bar() {
a = () => {
console.log(this, 'this指向定义的时候外层第一个普通函数'); //
}; // 在bar中定义 this继承于bar函数的this指向
}
function foo() {
a();
}
bar.call(barObj); // 将bar的this指向barObj
foo.call(fooObj); // 将foo的this指向fooObj
// 结果:{ msg: 'bar的this指向' }
从这个例子中我们就可以看出,箭头函数的this指向继承于在定义a时bar()
中this(barObj
),而在foo中调用a时,虽然调用a函数的的foo()
的this指向为fooObj,但这并没有修改a的指向,所以箭头函数的this指向和定义箭头的时有关,和运行时无关。
3.不能直接修改箭头函数的this指向
我们接下来尝试下直接修改箭头函数的this指向,看看有没有效果。
a.call(fooObj); // 结果:{ msg: 'bar的this指向' }
很明显,直接修改箭头函数的this指向并没有生效。
那么,如果我们想要修改箭头函数的this指向,就真的没有办法了吗?
仔细想一下,箭头函数的this指向是继承自外层第一个普通函数的,那如果我们去修改被继承的普通函数的this指向能不能改变箭头函数的this指向呢?
bar.call(fooObj); // 将bar普通函数的this指向barObj 然后内部的箭头函数也会指向barObj
a(); // {msg: "foo的this指向"}
可以看到我们修改被继承的普通函数的this指向,箭头函数的this指向也跟着改变了。
4. 箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window
(全局对象)
在非严格模式下,普通函数默认绑定的this指向全局对象,在严格模式下this指向undefined。那么在非严格模式和严格模式下箭头函数的this又指向什么呢?
// 非严格模式
a = () => {console.log(this);};
a(); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
// 严格模式
'use strict';
a = () => {console.log(this);};
a(); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
经过我们测试,可以看到箭头函数在严格模式和非严格模式下它的this都会指向window(全局对象)。
箭头函数的arguments
1.当箭头函数的this指向全局,使用arguments会报未声明的错误。
我们直接来举个例子尝试一下:
let b = () => {
console.log(arguments);
};
b(1, 2, 3, 4); // Uncaught ReferenceError: arguments is not defined
可以看到,这时使用确实arguments会报错,未声明arguments。
接着我们来看下箭头函数的this指向普通函数时的情况。
2.箭头函数的this指向继承自普通函数时,它的argumens继承于该普通函数
function bar() {
console.log(arguments); // ['外层第二个普通函数的参数']
bb('外层第一个普通函数的参数');
function bb() {
console.log(arguments); // ["外层第一个普通函数的参数"]
let a = () => {
console.log(arguments, 'arguments继承this指向的那个普通函数'); // ["外层第一个普通函数的参数"]
};
a('箭头函数的参数'); // this指向bb
}
}
bar('外层第二个普通函数的参数');
// Arguments ["外层第二个普通函数的参数", callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Arguments ["外层第一个普通函数的参数", callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Arguments ["外层第一个普通函数的参数", callee: ƒ, Symbol(Symbol.iterator): ƒ] "arguments继承this指向的那个普通函数"
可以看到箭头函数的arguments继承于普通函数arguments。
箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
如下示例,普通函数的函数参数支持重命名,后面出现的会覆盖前面的,箭头函数会抛出错误:
function func1(a, a) {
console.log(a, arguments); // 2 [1,2]
}
var func2 = (a,a) => {
console.log(a); // 报错:在此不允许重复参数名称
};
func1(1, 2); func2(1, 2);
箭头函数的注意事项及不适用场景
箭头函数的注意事项
- 一条语句返回对象字面量,需要加括号,或者直接写成多条语句的return形式,否则像func中演示的一样,花括号会被解析为多条语句的花括号,不能正确解析。
var func1 = () => { foo: 1 }; // 想返回一个对象,花括号被当成多条语句来解析,执行后返回undefined
var func2 = () => ({foo: 1}); // 用圆括号是正确的写法
var func2 = () => {
return {
foo: 1 // 更推荐直接当成多条语句的形式来写,可读性高
};
};
- 箭头函数在参数和箭头之间不能换行!
var func = ()
=> 1; // 报错: Unexpected token =>
- 箭头函数的解析顺序相对靠前
let a = false || function() {}; // ok
let b = false || () => {}; // error
let c = false || (() => {}); // ok
箭头函数不适用场景:
我们先来看个例子:
const obj = {
array: [1, 2, 3],
sum: () => {
// 根据上面说到的:外层没有普通函数this会指向全局对象
return this.array.push('全局对象下没有array,这里会报错'); // 找不到push方法
}
};
obj.sum();
因为这个箭头函数没有外层的普通函数,所以此时这个箭头函数的this指向全局对象。
当我们改写成下面这样的时候就没有问题了。
sum() {
return this.array.push('this指向obj');
}
同样的,还有这个回调函数中的this:
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
this.innerHTML = 'Clicked button'; // this又指向了全局
});
同样的把箭头函数修改成普通函数就能解决。
小结
普通函数和箭头函数的区别:
- 箭头函数没有prototype(原型),所以箭头函数本身没有this,箭头函数的this在定义的时候继承自外层第一个普通函数的this。
- 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)。
- 箭头函数本身的this指向不能改变,但可以修改它要继承的对象的this。
- 箭头函数的this指向全局,使用arguments会报未声明的错误。箭头函数的this指向普通函数时,它的argumens继承于该普通函数。