前言
大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步!
个人主页:南木元元
目录
什么是箭头函数
箭头函数和普通函数的区别
更简洁的语法
箭头函数没有自己的this
箭头函数的this不会改变
箭头函数没有prototype属性
箭头函数不能作为构造函数
箭头函数不能使用arguments对象
箭头函数不能用作Generator函数
箭头函数不适用的场景
结语
箭头函数是ES6(ECMAScript 6)新增的使用箭头(=>)语法定义函数表达式的能力。任何可以使用函数表达式的地方,都可以使用箭头函数,并且它的语法比传统的函数表达式更加简洁。
// 函数表达式
let functionExpressionSum = function(a, b) {
return a + b;
}
// 箭头函数
let arrowSum = (a, b) => {
return a + b;
}
下面就来详细讲解一下箭头函数和普通函数的区别。
// 只有一个参数,可以不用括号,以下两种写法都有效
let double = (x) => { return 2 * x; };
let triple = x => { return 3 * x; };
// 没有参数需要括号
let getRandom = () => { return Math.random(); };
// 多个参数需要括号
let sum = (a, b) => { return a + b; };
// 无效的写法:
let multiply = a, b => { return a * b; };
// 以下两种写法都有效,而且返回相应的值
let double = (x) => { return 2 * x; };
let triple = (x) => 3 * x;
// 无效的写法
let multiply = (a, b) => return a * b;
// 最常见的就是调用一个函数
let fn = () => void doesNotReturn();
由于其更简洁的语法,箭头函数的一个用处就是简化回调函数。
// 普通函数写法
var result = arr.sort(function (a, b) {
return a - b;
});
// 箭头函数写法
var result = arr.sort((a, b) => a - b);
所有函数在执行时,会创建一个函数执行上下文,普通函数的执行上下文中会有一个变量this,而箭头函数没有,箭头函数不会创建自己的this对象,只会继承在自己作用域的上一层this。
var id = 'Global'
// 箭头函数定义在全局作用域
let fun1 = () => {
console.log(this.id)
}
fun1() // 'Global'
输出:
可以⽤Babel理解⼀下箭头函数:
// ES6
const obj = {
getArrow() {
return () => {
console.log(this === obj);
};
}
}
// 转化后的ES5
var obj = {
getArrow: function getArrow() {
var _this = this;
return function () {
console.log(_this === obj);
};
}
};
上面代码中,转换后的ES5版本清楚地说明了,箭头函数里没有自己的this,而是引用外层的this。
箭头函数没有自己的this,所以箭头函数中this的指向在它定义时就已经确定了,之后不会改变。
var name = 'GLOBAL';
var obj = {
name: '南木元元',
getName1: function(){
console.log(this.name);
},
getName2: () => {
console.log(this.name);
}
};
obj.getName1(); // '南木元元'
obj.getName2(); // 'GLOBAL'
输出结果:
对象obj的方法b是使用箭头函数定义的,这个函数中的this就永远指向它定义时所处的全局执行环境中的this,即便这个函数是作为对象obj的方法调用,this依旧指向Window对象。所以其实定义对象的方法是不适合使用箭头函数的。
此外,call()、apply()、bind()等方法也不能改变箭头函数中this的指向。
var id = 'Global';
let fun1 = () => {
console.log(this.id)
};
fun1(); // 'Global'
// this的指向不会改变
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'
输出结果:
来看下面代码。
let fn = function(name) {
console.log(name);
}
let fn2 = name => console.log(name);
console.log(fn.prototype);
console.dir(fn2.prototype);
输出结果:
上面说了,箭头函数没有自己的this,没有prototype属性,所以也就不能用作构造函数,即不可以对箭头函数使用new
命令,否则会抛出错误。
let fn = (name, age) => {
this.name = name;
this.age = age;
}
// 报错
let p = new fn('南木元元', 18);
输出结果:
为什么会这样呢?这其实跟new内部实现有关,new的实现步骤如下:
function myNew(constructor, ...args) {
// 基于原型链 创建一个新对象,并且继承构造函数constructor的原型对象prototype上的属性
let newObj = Object.create(constructor.prototype);
// 执行构造函数,并让this指向这个新对象
let res = constructor.apply(newObj, args);
// 如果函数的执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
return typeof res === 'object' ? res: newObj;
}
上面的第二、三步,箭头函数都是没有办法执行的。
arguments是一个对应于传递给函数的参数的类数组对象。
arguments是在所有普通函数中都可用的一个类数组对象,类数组不是数组,而是类似数组的对象,它除了length属性和索引之外,不能调用数组的方法。
所以通常会使用Array.prototype.slice.call(arguments)/Array.from(arguments)/[...arguments]的方式,将它转换成一个数组。
let fn = function () {
console.log(arguments);
console.log(Array.prototype.slice.call(arguments));
}
fn('param1', 'param2');
输出结果:
箭头函数不可以使用arguments对象,该对象在函数体内不存在。
let fn = (name, age) => console.log(arguments);
// 报错
fn('南木元元', 18);
输出结果:
在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。
let fn = function(name, age) {
let fn2 = name => {
console.log(arguments);
}
fn2();
}
fn('南木元元', 18);
输出结果:
那么如果一定要用呢?可以用ES6中的rest参数代替。
let fn = (...args) => console.log(args);
fn('南木元元', 18);
输出结果:
上述代码使用了rest参数(形式为...变量名)获取函数的多余参数,这样就不需要使用arguments对象了。
箭头函数内部不可以使用yield命令,因此箭头函数不能用作Generator函数。
let fn = function *() {
yield '南木元元';
}
let p = fn();
console.log(p.next());
输出:
let fn = *() => {
yield '南木元元';
}
let p = fn();
console.log(p.next());
输出:
第一个场景上面提到过,定义对象的方法并且方法内部使用this时不适合用箭头函数。
var name = 'GLOBAL';
var obj = {
name: '南木元元',
getName: () => {
console.log(this.name);
}
};
obj.getName(); // 'GLOBAL'
上述代码中,调用obj.getName()方法时,如果是普通函数,该方法内部的this指向调用它的那个对象;如果写成上面那样的箭头函数,使得this指向了全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致getName箭头函数定义时的作用域就是全局作用域。
第二个场合是需要动态this的时候,也不应使用箭头函数。
var button = document.getElementById('btn');
button.addEventListener('click', () => {
console.log(this); //由于使用了箭头函数,this会指向全局对象Window
});
上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。
本文主要总结了箭头函数和普通函数的几大区别,箭头函数虽然语法简洁,但也有一些场合不适用,需要根据不同的场景选择使用合适的函数。
如果此文对你有帮助的话,欢迎关注、点赞、⭐收藏、✍️评论,支持一下博主~