Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >普通函数和箭头函数的区别

普通函数和箭头函数的区别

作者头像
全栈程序员站长
发布于 2022-09-07 01:52:06
发布于 2022-09-07 01:52:06
86600
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

详解箭头函数和普通函数的区别以及箭头函数的注意事项、不适用场景

箭头函数是ES6的API,相信很多人都知道,因为其语法上相对于普通函数更简洁,深受大家的喜爱。就是这种我们日常开发中一直在使用的API,大部分同学却对它的了解程度还是不够深…

普通函数和箭头函数的区别:

箭头函数的this指向规则:

  1. 箭头函数没有prototype(原型),所以箭头函数本身没有this
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = () =>{};
console.log(a.prototype); // undefined
  1. 箭头函数的this指向在定义的时候继承自外层第一个普通函数的this。 下面栗子中在一个函数中定义箭头函数,然后在另一个函数中执行箭头函数。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a,
  barObj = { msg: 'bar的this指向' };
fooObj = { msg: 'foo的this指向' };
bar.call(barObj); // 将bar的this指向barObj
foo.call(fooObj); // 将foo的this指向fooObj
function foo() {
  a(); // 结果:{ msg: 'bar的this指向' }
}
function bar() {
  a = () => {
    console.log(this, 'this指向定义的时候外层第一个普通函数'); // 
  }; // 在bar中定义 this继承于bar函数的this指向
}

从上面例子中可以得出两点

  • 箭头函数的this指向定义时所在的外层第一个普通函数,跟使用位置没有关系。
  • 被继承的普通函数的this指向改变,箭头函数的this指向会跟着改变
  1. 不能直接修改箭头函数的this指向 上个例子中的foo函数修改一下,尝试直接修改箭头函数的this指向。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let fnObj = { msg: '尝试直接修改箭头函数的this指向' };
function foo() {
  a.call(fnObj); // 结果:{ msg: 'bar的this指向' }
}

很明显,call显示绑定this指向失败了,包括aaply、bind都一样。

它们(call、aaply、bind)会默认忽略第一个参数,但是可以正常传参。

然后我又通过隐式绑定来尝试同样也失败了,new调用会报错,这个稍后再说。

SO,箭头函数不能直接修改它的this指向。

幸运的是,我们可以通过间接的形式来修改箭头函数的指向:

去修改被继承的普通函数的this指向,然后箭头函数的this指向也会跟着改变,这在上一个栗子中有演示。

bar.call(barObj);// 将bar普通函数的this指向barObj 然后内部的箭头函数也会指向barObj

  1. 箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)

唔,这个问题实际上是面试官提出来的,当时我认为的箭头函数规则就是:箭头函数的this指向继承自外层第一个普通函数的this,现在看来真是不严谨(少说一个定义的时候),要是面试官问我:定义和执行不在同一个普通函数中,它又指向哪里,肯定歇菜…

既然箭头函数的this指向在定义的时候继承自外层第一个普通函数的this,那么:

当箭头函数外层没有普通函数,它的this会指向哪里?

这里跟我之前写的this绑定规则不太一样(不懂的可以点进去看一下),普通函数的默认绑定规则是:

在非严格模式下,默认绑定的this指向全局对象,严格模式下this指向undefined

如果箭头函数外层没有普通函数继承,它this指向的规则:

经过测试,箭头函数在全局作用域下,严格模式和非严格模式下它的this都会指向window(全局对象)。

Tip:测试的时候发现严格模式在中途声明无效,必须在全局/函数的开头声明才会生效:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
a = 1;
'use strict'; // 严格模式无效 必须在一开始就声明严格模式
b = 2; // 不报错

箭头函数的this指向全局,使用arguments会报未声明的错误

如果箭头函数的this指向window(全局对象)使用arguments会报错,未声明arguments

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let b = () => {
  console.log(arguments);
};
b(1, 2, 3, 4); // Uncaught ReferenceError: arguments is not defined

PS:如果你声明了一个全局变量为arguments,那就不会报错了,但是你为什么要这么做呢?

箭头函数的this指向普通函数时,它的argumens继承于该普通函数 上面是第一种情况:箭头函数的this指向全局对象,会报arguments未声明的错误。

第二种情况是:箭头函数的this如果指向普通函数,它的argumens继承于该普通函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function bar() {
  console.log(arguments); // ['外层第二个普通函数的参数']
  bb('外层第一个普通函数的参数');
  function bb() {
    console.log(arguments); // ["外层第一个普通函数的参数"]
    let a = () => {
      console.log(arguments, 'arguments继承this指向的那个普通函数'); // ["外层第一个普通函数的参数"]
    };
    a('箭头函数的参数'); // this指向bb
  }
}
bar('外层第二个普通函数的参数');

那么应该如何来获取箭头函数不定数量的参数呢?答案是:ES6rest参数(…扩展符)

rest参数获取函数的多余参数

这是ES6的API,用于获取函数不定数量的参数数组,这个API是用来替代arguments的,API用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = (first, ...abc) => {
  console.log(first, abc); // 1 [2, 3, 4]
};
a(1, 2, 3, 4);

上面的例子展示了,获取函数除第一个确定的参数,以及用一个变量接收其他剩余参数的示例。

也可以直接接收函数的所有参数,rest参数的用法相对于arguments的优点:

  1. 箭头函数和普通函数都可以使用。
  2. 更加灵活,接收参数的数量完全自定义。
  3. 可读性更好 参数都是在函数括号中定义的,不会突然出现一个arguments,以前刚见到的时候,真的好奇怪了!
  4. rest是一个真正的数组,可以使用数组的API。 因为arguments是一个类数组的对象,有些人以为它是真正的数组,所以会出现以下场景:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arguments.push(0); // arguments.push is not a function

如上,如果我们需要使用数组的API,需要使用扩展符/Array.from来将它转换成真正的数组:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arguments = [...arguments]; 或者 :arguments = Array.from(arguments);

rest参数有两点需要注意:

  • rest必须是函数的最后一位参数:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = (first, ...rest, three) => {
  console.log(first, rest,three); // 报错:Rest parameter must be last formal parameter
};
a(1, 2, 3, 4);
  • 函数的length属性,不包括rest 参数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

扩展运算符还可以用于数组,这里是阮一峰老师的文档

PS:感觉这里写多了,但比较喜欢把一个知识点讲清楚…

使用new调用箭头函数会报错 无论箭头函数的thsi指向哪里,使用new调用箭头函数都会报错,因为箭头函数没有constructor

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = () => {};
let b = new  a(); // a is not a constructor

箭头函数不支持new.target new.target是ES6新引入的属性,普通函数如果通过new调用,new.target会返回该函数的引用。

此属性主要:用于确定构造函数是否为new调用的。

  • 箭头函数的this指向全局对象,在箭头函数中使用箭头函数会报错
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = () => {
  console.log(new.target); // 报错:new.target 不允许在这里使用
};
a();
  • 箭头函数的this指向普通函数,它的new.target就是指向该普通函数的引用。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
new bb();
function bb() {
  let a = () => {
    console.log(new.target); // 指向函数bb:function bb(){...}
  };
  a();
}

更多关于new.target可以看一下阮一峰老师关于这部分的解释

箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名 如下示例,普通函数的函数参数支持重命名,后面出现的会覆盖前面的,箭头函数会抛出错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function func1(a, a) {
  console.log(a, arguments); // 2 [1,2]
}

var func2 = (a,a) => {
  console.log(a); // 报错:在此上下文中不允许重复参数名称
};
func1(1, 2); func2(1, 2);

箭头函数相对于普通函数语法更简洁优雅: 讲道理,语法上的不同,也属与它们两个的区别!

  • 箭头函数都是匿名函数,并且都不用写function
  • 只有一个参数的时候可以省略括号:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var f = a => a; // 传入a 返回a
  • 函数只有一条语句时可以省略{}return
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var f = (a,b,c) => a; // 传入a,b,c 返回a
  • 简化回调函数,让你的回调函数更优雅:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[1,2,3].map(function (x) {
  return x * x;
}); // 普通函数写法 
[1,2,3].map(x => x * x); // 箭头函数只需要一行

箭头函数的注意事项及不适用场景

箭头函数的注意事项

  • 一条语句返回对象字面量,需要加括号,或者直接写成多条语句的return形式,否则像func中演示的一样,花括号会被解析为多条语句的花括号,不能正确解析
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var func1 = () => { foo: 1 }; // 想返回一个对象,花括号被当成多条语句来解析,执行后返回undefined
var func2 = () => ({foo: 1}); // 用圆括号是正确的写法
var func2 = () => {
  return {
    foo: 1 // 更推荐直接当成多条语句的形式来写,可读性高
  };
};
  • 箭头函数在参数和箭头之间不能换行!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var func = ()
           => 1;  // 报错: Unexpected token =>
  • 箭头函数的解析顺序相对靠前 MDN: 虽然箭头函数中的箭头不是运算符,但箭头函数具有与常规函数不同的特殊运算符优先级解析规则
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = false || function() {}; // ok
let b = false || () => {}; // Malformed arrow function parameter list
let c = false || (() => {}); // ok

箭头函数不适用场景: 围绕两点:箭头函数的this意外指向和代码的可读性。

  • 定义字面量方法,this的意外指向。 因为箭头函数的简洁
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const obj = {
  array: [1, 2, 3],
  sum: () => {
    // 根据上文学到的:外层没有普通函数this会指向全局对象
    return this.array.push('全局对象下没有array,这里会报错'); // 找不到push方法
  }
};
obj.sum();

上述例子使用普通函数或者ES6中的方法简写的来定义方法,就没有问题了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 这两种写法是等价的
sum() {
  return this.array.push('this指向obj');
}
sum: function() {
  return this.array.push('this指向obj');
}

还有一种情况是给普通函数的原型定义方法的时候,通常会在普通函数的外部进行定义,比如说继承/添加方法的时候。

这时候因为没有在普通函数的内部进行定义,所以this会指向其他普通函数,或者全局对象上,导致bug!

  • 回调函数的动态this 下文是一个修改dom文本的操作,因为this指向错误,导致修改失败:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
    this.innerHTML = 'Clicked button'; // this又指向了全局
});

相信你也知道了,改成普通函数就成了。

  • 考虑代码的可读性,使用普通函数
  • 函数体复杂: 具体表现就是箭头函数中使用多个三元运算符号,就是不换行,非要在一行内写完,非常恶心!
  • 行数较多
  • 函数内部有大量操作

文章内容小结:

普通函数和箭头函数的区别:

  1. 箭头函数没有prototype(原型),所以箭头函数本身没有this
  2. 箭头函数的this在定义的时候继承自外层第一个普通函数的this
  3. 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)
  4. 箭头函数本身的this指向不能改变,但可以修改它要继承的对象的this
  5. 箭头函数的this指向全局,使用arguments会报未声明的错误。
  6. 箭头函数的this指向普通函数时,它的argumens继承于该普通函数
  7. 使用new调用箭头函数会报错,因为箭头函数没有constructor
  8. 箭头函数不支持new.target
  9. 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
  10. 箭头函数相对于普通函数语法更简洁优雅

箭头函数的注意事项及不适用场景 箭头函数的注意事项:

  1. 箭头函数一条语句返回对象字面量,需要加括号
  2. 箭头函数在参数和箭头之间不能换行
  3. 箭头函数的解析顺序相对||靠前 不适用场景:箭头函数的this意外指向和代码的可读性。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/147754.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JavaScript 设计模式学习第二篇-关于this、new、bind、call、apply
虽然标题关于this、new、bind、call、apply,但实际上这些都离不开 this,因此本文将着重讨论 this,在此过程中分别讲解其他知识点。
越陌度阡
2020/11/26
2950
函数的扩展
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
小小杰啊
2022/12/21
8140
重学 this 关键字(看完不全懂您找我哦)
1. 面试会问啊!总有一些面试官喜欢问你一段不可能这么写的代码。看一道经典且古老的面试题(学完本文后,文末会有一道更复杂的面试题等着你哦!)
coder_koala
2019/09/10
5090
重学 this 关键字(看完不全懂您找我哦)
ES6 系列之箭头函数
很多时候,你可能想不到要这样用,所以再来举个例子,比如在 React 与 Immutable 的技术选型中,我们处理一个事件会这样做:
夜尽天明
2019/06/28
4660
ES6中的箭头函数
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
默默的成长
2022/12/05
6120
JS 语法糖 1 —— 箭头函数
从 ECMAScript 6 开始,JS 新增了一种新的函数:箭头函数(Arrow Function)。
恋喵大鲤鱼
2021/02/22
2K0
ES6的箭头函数的详细介绍
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
Javanx
2019/09/04
4390
ES6的箭头函数的详细介绍
ES6中的箭头函数=>
ES6标准新增了一种新的函数:Arrow Function(箭头函数)。为什么叫Arrow Function?因为它的定义用的就是一个箭头:
三分恶
2020/10/26
6120
JavaScript的箭头函数与普通函数区别?
箭头函数用更简洁的方式,来完成普通函数的功能,但是不具备普通函数拥有的属性: this 、 arguments 、 super 、 new.target,有两种表达形式:
Learn-anything.cn
2021/11/27
6090
你知道多少this,new,bind,call,apply?那我告诉你
那么什么是this,new,bind,call,apply呢?这些你都用过吗?掌握这些内容都是基础中的基础了。如果你不了解,那还不赶快去复习复习,上网查阅资料啥的!
达达前端
2019/11/14
3880
es6箭头函数详解
箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
用户10106350
2022/10/28
2850
JavaScript 中 this 的错误认识、绑定规则、常见问题讲解
相信 Javascript 中的 this 会使很多同学在工作学习中产生困惑,笔者也同样是,经过阅读各种资料及实际工作中的应用,做了以下梳理,主要内容包括长期以来大家对 this 的错误认识及 this 的绑定规则,箭头函数、实际工作场景中遇到的问题,希望对于有此困惑的你能有所帮助。
五月君
2020/08/06
6070
你知道ES6箭头函数的优缺点吗?
我们都知道,在 JavaScript 里定义函数有多种方式。最常见的是用function关键字:
胡哥有话说
2020/06/04
8.7K0
【译】《Understanding ECMAScript6》- 第二章-函数
函数在任何一门编程语言中都是很重要的一个环节。JavaScript至今已有多年的历史,但是它的函数仍然停留在很初级的阶段。函数问题的大量堆积,以及某些函数非常微妙的功能差异,很容易产生错误,并且有时候一个很简单的功能往往需要通过大量的代码来实现。 ES6吸取了多年来JavaScript开发者的反馈,在ES5函数的基础上进行了大量的改进,令JavaScript程序更加健壮并且减少了错误发生率。 默认参数 JavaScript函数的特性之一,便是接受传入的参数可以与函数定义的参数数量不同。利用这种特性,函数可以
寒月十八
2018/01/30
1.3K0
普通函数和箭头函数的区别
MDN的描述是箭头函数不会创建自己的this他只会从自己的作用域链的上一层继承this,这里我们可以理解为this指向外层第一个普通函数(如果没有,则指向全局对象(可通过globalThis访问));而普通函数中this指向其调用者。
用户10562852
2023/05/12
3780
普通函数和箭头函数的区别
ES6基础:箭头函数
我认为是这样的~,但这只是其中的一个很小原因,先来看看它有多“高大上”,也就是常见的用法
全栈程序员站长
2022/07/02
3230
函数(function)的前世今生
函数就是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
江米小枣
2020/06/16
7080
函数(function)的前世今生
js 箭头函数详解
es6 新增了使用胖箭头(=>)语法定义函数表达式的能力,很大程度上,箭头函数实例化的函数对象与正式的函数表达式创建的函数对象行为是相同的。任何可以使用函数表达式的地方,都可以使用箭头函数:
IT工作者
2022/05/12
1.2K0
大厂HR面试必备ES6中的深入浅出面试题知识点
ESMAScript6简介,ES6是JavaScript语言的下一代标准,目的是让JavaScript语言可以写复杂的大型应用程序,成为企业级语言。那么ECMAScript和JavaScript的关系到底是什么呢?两者有着什么样的联系?
达达前端
2019/11/19
6350
ES6——函数
只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递。
羊羽shine
2019/07/03
3980
相关推荐
JavaScript 设计模式学习第二篇-关于this、new、bind、call、apply
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验