1440 字
7 分钟
箭头函数理解

箭头函数#

es6语法

// ES6语法
const fn = v => v;

一. 特点#

1. 语法简介#

箭头函数省去了function关键字,用=>代替function,圆括号代表参数部分,当只有一个参数时,圆括号可省略,当只有一行返回语句时,return和花括号{}都可以省略。

// 求两数之和
const fn = (a, b) => {return a + b;}; // 等价于 const fn = (a, b) => a + b;
// 求一个数组各项之和
const sum = [1, 2, 3, 4, 5].reduce((x, y) => x + y, 0); // 15
// 将数组中的元素按从小到大顺序排序
const array = [2, 4, 1, 5, 9, 7].sort((x, y) => x - y); //  [1, 2, 4, 5, 7, 9]
// 过滤数组中为偶数的数字
const array = [0, 1, 2, 3, 4, 5, 6].filter(x => x % 2 === 0); // [0, 2, 4, 6]

2. 不绑定this#

箭头函数体内的this永远指向的是定义时所在的对象,而不是调用时所在的对象。

function Fn() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function() {
this.s2++;
}, 1000);
}
var fn = new Fn();
setTimeout(() => console.log('s1= ', fn.s1), 3100); // 3.1秒后输出s1= 3
setTimeout(() => console.log('s2= ', fn.s2), 3100); // 3.1秒后输出s2= 0

上面代码中,Fn函数内部设置了两个定时器,分别使用了箭头函数和普通函数。

箭头函数中的this指向定义时所在的作用域(即Fn函数),this.s1++是处在箭头函数中,这里的this就是fn,所以fn.s1的值为3。

而普通函数中的this指向运行时所在的作用域(即全局对象window),this.s2++实际等于window.s2++,fn.s2一次都没有更新,因而得到的是0。

所以,从严格意义上讲,箭头函数中不会创建自己的this,而是会从自己作用域链的上一层继承。

const Person = {
name: 'Kimmy',
age: 20,
doSomething: function() {
setTimeout(() => console.log(`name: ${this.name}, age: ${this.age}`), 1000);
}
};
Person.doSomething(); // name: Kimmy, age: 20
const Person2 = {
name: 'Kimmy',
age: 20,
doSomething: () => {
setTimeout(() => console.log(`name: ${this.name}, age: ${this.age}`), 1000);
}
};
Person2.doSomething(); // name: , age: undefined

上面两段代码的唯一区别在于doSomething()函数的写法,Person中使用了普通函数定义,Person2中使用了箭头函数定义。

在第一段代码中,Person.doSomeThing()中的this指向函数的调用体,即Person本身,在调用setTimeout()函数时,由于其函数体部分是通过箭头函数定义的,内部的this会继承父作用域的this(即Person),从而输出name: Kimmy, age: 20

在第二段代码中,Person2.doSomething()中的this指向外层作用域,而Person2的父作用域是全局作用域window,在调用setTimeout()函数时,由于其函数体部分是通过箭头函数定义的,内部的this会继承doSomething()函数所在的作用域this(即window),而window上name属性是'',age属性不存在,所以Person2.doSomething()输出name: , age: undefined

综上所述,箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正因为它没有this,所以也就不能用作构造函数

3. 不支持 call()、 apply()、bind() 函数的特性#

通过调用 call()apply()bind() 函数可以改变一个函数的执行主体,即改变被调用函数中 this 的指向

但箭头函数中不支持 call()apply()bind()等,因为箭头函数中没有自己的 this ,而是继承父作用域中的 this

function fn() {
return () => {
console.log(`id= ${this.id}`);
}
}
let f = fn.call({id: 1}); // id= 1
let f2 = f.call({id: 2})()(); // id = 1
let f3 = f().call({id: 3})(); // id= 1
let f4 = f()().call({id: 4}); // id= 1

上面代码中只有一个this,即函数fnthis,所以f2f3f4都输出同样的结果。因为内层函数时箭头函数,没有自己的this,它们的this都是最外层fn函数是this

4. 不绑定 arguments#

在普通函数function中,我们可以通过arguments对象来获取到实际传入的参数值,但在箭头函数中是不存在的。

const fn = () => {
console.log(arguments);
}
fn(1, 2); // Uncaught ReferenceError: arguments is not defined
function fn() {
setTimeout(() => {
console.log(arguments);
}, 0);
}
fn(1, 2); // [1, 2]

5.支持嵌套#

const pipeline = (...funcs) => val => funcs.reduce((a, b => b(a)), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);
addThenMult(5); // 12

二.箭头函数不适用场景#

(1)定义对象的方法且该方法内部包括this#

const Person = {
name: 'Kimmy',
age: 20,
doSomething: () => {
this.age++;
}
};

Person.doSomething()方法是一个箭头函数,调用Person.doSomething()时,如果是普通函数,该方法内部的this指向Person,如果写成上面那样的箭头函数,使得this指向全局对象。

(2)不能作为构造函数,不能使用 new 操作符#

构造函数时通过new操作符生成对象实例的,生成实例的过程也是通过构造函数给实例绑定this的过程,而箭头函数没有自己的this,因此不能使用箭头函数作为构造函数,也不能通过new操作符来调用箭头函数。

// 普通函数
function Person(name) {
this.name = name;
}
let p = new Person('Kimmy'); // 正常
// 箭头函数
let Person = (name) => {
this.name = name;
}
let p = new Person('Kimmy'); // Uncaught TypeError: Person is not a constructor

(3)没有 prototype 属性#

let a = () => 1;
function b() {
return 2;
}
a.prototype // undefined
b.prototype // {constructor: ƒ}

(4)不适合将原型函数定义成箭头函数#

在给构造函数添加原型函数时,如果使用箭头函数,其中的this会指向全局作用域window,而并不会指向构造函数,因此并不会访问到构造函数本身,也就无法访问到实例属性,这就失去了作为原型函数的意义。

箭头函数理解
https://nollieleo.github.io/posts/箭头函数理解/
作者
翁先森
发布于
2021-03-08
许可协议
CC BY-NC-SA 4.0