一篇讲透:箭头函数、普通函数有什么区别

前言

 📫 大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步!

 🍅 个人主页:南木元元


目录

什么是箭头函数

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

更简洁的语法

箭头函数没有自己的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;
  • 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。
// 最常见的就是调用一个函数
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对象,只会继承在自己作用域的上一层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,所以箭头函数中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'

输出结果:

箭头函数没有prototype属性

来看下面代码。

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的实现步骤如下:

  • 创建一个新的空对象
  • 设置原型,将对象的原型设置为函数的prototype对象
  • 让函数的this指向这个对象,执行构造函数的代码
  • 返回新的对象
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是一个对应于传递给函数的参数的类数组对象。

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对象了。

箭头函数不能用作Generator函数

箭头函数内部不可以使用yield命令,因此箭头函数不能用作Generator函数。

let fn = function *() {
    yield '南木元元';
}
let p = fn();
console.log(p.next());

 输出:

let fn = *() => {
    yield '南木元元';
}
let p = fn();
console.log(p.next()); 

 输出:

箭头函数不适用的场景

  • 对象方法,且方法内部使用this

第一个场景上面提到过,定义对象的方法并且方法内部使用this时不适合用箭头函数。

var name = 'GLOBAL';
var obj = {
    name: '南木元元',
    getName: () => {
        console.log(this.name);
    }
};
obj.getName();    // 'GLOBAL'

上述代码中,调用obj.getName()方法时,如果是普通函数,该方法内部的this指向调用它的那个对象;如果写成上面那样的箭头函数,使得this指向了全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致getName箭头函数定义时的作用域就是全局作用域。

  • 需要动态this

第二个场合是需要动态this的时候,也不应使用箭头函数。

var button = document.getElementById('btn');
button.addEventListener('click', () => {
    console.log(this);    //由于使用了箭头函数,this会指向全局对象Window
});

上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。

结语 

本文主要总结了箭头函数和普通函数的几大区别,箭头函数虽然语法简洁,但也有一些场合不适用,需要根据不同的场景选择使用合适的函数。

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论支持一下博主~   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/270078.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【WPF.NET开发】数据绑定应用场景

目录 1、实现属性更改通知 示例 2、双向绑定​​​更新源 示例 3、对分层数据使用主-从模式 示例 4、对分层 XML 数据使用主-从模式 示例 5、绑定两个控件的属性 示例 6、创建和绑定到 ObservableCollection 示例 7、使用 XMLDataProvider 和 XPath 查询绑定到 XML…

Java@RequestParam注解和@RequestBody注解接收参数

目录 Java后端接收数据 第一章、后端不写任何注解情况下接收参数1.1)后端不写注解postman发出get请求1.2)后端不写注解postman发出post请求 第二章、后端写RequestParam注解接收参数2.1)postman发出post请求2.2)postman发出get请求…

锂电池搅拌机的设备健康管理解决方案

随着电动车辆和可再生能源市场的迅速发展,锂电池作为一种重要的能源存储产品,正变得越来越重要。而锂电池搅拌机作为锂电池生产线中的核心设备之一,其正常运行对于生产线的高效稳定至关重要。为了确保锂电池搅拌机的可靠性和设备寿命&#xf…

SQL进阶理论篇(二十一):基于SQLMap的自动化SQL注入

文章目录 简介获取当前数据库和用户信息获取MySQL中的所有数据库名称查询wucai数据库中的所有数据表查看heros数据表中的所有字段查询heros表中的英雄信息总结参考文献 简介 从上一小节,可以发现,如果我们编写的代码存在着SQL注入的漏洞,后果…

android内存管理机制概览

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、相关概念3.1 垃圾回收3.2 应用内存的分配与回…

crtc 原理

CRTC Streams the framebuffer following the screen’s timings Driving screens : the CRT ControllerDriving screens : the CRT Controller Streams the framebuffer following the screen’s timings After each line, the CRTC must wait for the CRT to go back to th…

GoDance分布式搜索引擎项目

目录 前言一、布尔模型二、 实用评分函数1. 查询归一化因子2. 协调因子3. TF-IDF3.1 TF3.2 IDF3.3 字段长度归一值BOOST 4. 向量空间模型具体方案 三、按受欢迎度提升权重四、实时搜索与相关搜索五、具体实现方案1. 布尔模型2. 评分函数3. 实时相关搜索 前言 5月6日参加了字节…

<script setup> 的作用

一、使用<script setup> 之后&#xff0c;就不需要手动写以下代码&#xff0c;只要写逻辑代码 未加setup&#xff0c;vite 工程要加上下面代码 *export default{ * setup(){ * //只要写逻辑代码 * return{***} * } * } 加了setup &#xff0c;export default 、…

doris基本操作,05-Rollup

简述 Rollup类似于mysql的视图&#xff0c;区别在于视图并没有将数据独立存储&#xff0c;视图是逻辑上的连接。而Rollup将数据独立存储了&#xff0c;玩的是真的。当查询命中Rollup时&#xff0c;会从Rollup表里获取数据&#xff0c;提高查询效率。 操作 创建Rollup表 alt…

web自动化测试的智能革命:AI如何推动软件质量保证的未来

首先这个标题不是我取的&#xff0c;是我喂了关键字让AI给取的&#xff0c;果然非常的标题党&#xff0c;让人印象深刻&#xff0c;另外题图也是AI自动生成的。 先简单回顾一下web自动化测试的一些发展阶段 QTP时代 很多年前QTP横空出世的时候&#xff0c;没有人会怀疑这种工…

插入排序详解(C语言)

前言 插入排序是一种简单直观的排序算法&#xff0c;在小规模数据排序或部分有序的情况下插入排序的表现十分良好&#xff0c;今天我将带大家学习插入排序的使用。let’s go ! ! ! 插入排序 插入排序的基本思想是将待排序的序列分为已排序和未排序两部分。初始时&#xff0c…

【序列化和反序列化】

&#x1f341;什么是序列化和反序列化&#xff1f; &#x1f341;典型解析&#x1f341;拓展知识仓&#x1f341;如何进行序列化和反序列化&#x1f341;未实现Serializable&#xff0c;可以序列化吗? &#x1f341;典型解析 在Java中&#xff0c;我们可以通过多种方式来创建对…

java接口限流详解

目录 1.简介1.1.为什么需要限流?1.2.限流和熔断有什么区别&#xff1f;1.3.限流和削峰有什么区别&#xff1f;1.4 缓存&#xff0c;降级&#xff0c;限流简介 2.应用级限流2.1 控制并发数量2.2 控制访问速率2.2.1 令牌桶算法2.2.2 漏桶算法 3.分布式限流4.交流群 1.简介 接口…

渗透测试——1.3计算机网络基础

一、黑客术语 1、肉鸡&#xff1a;被黑客攻击电脑&#xff0c;可以受黑客控制不被发现 2、端口&#xff08;port&#xff09;&#xff1a;数据传输的通道 3、弱口令&#xff1a;强度不高&#xff0c;容易被猜到的口令、密码 4、客户端&#xff1a;请求申请电脑&#xff08;…

宝塔mysql本地服务器状态异常如何解决

今天安装宝塔的时候突然遇到的问题 来吧 直接上bug图 答案&#xff1a;修改Mysql数据库密码

【论文阅读笔记】SegVol: Universal and Interactive Volumetric Medical Image Segmentation

Du Y, Bai F, Huang T, et al. SegVol: Universal and Interactive Volumetric Medical Image Segmentation[J]. arXiv preprint arXiv:2311.13385, 2023.[代码开源] 【论文概述】 本文思路借鉴于自然图像分割领域的SAM&#xff0c;介绍了一种名为SegVol的先进医学图像分割模型…

不同参数规模大语言模型在不同微调方法下所需要的显存总结

原文来自DataLearnerAI官方网站&#xff1a; 不同参数规模大语言模型在不同微调方法下所需要的显存总结 | 数据学习者官方网站(Datalearner)https://www.datalearner.com/blog/1051703254378255 大模型的微调是当前很多人都在做的事情。微调可以让大语言模型适应特定领域的任…

JY901S 9轴姿态角度传感器模块

JY901S 9轴姿态角度传感器模块 JY901S 简介模块特性引脚说明IIC通讯IIC读写寄存器代码示例 JY901S 简介 模块集成高精度的陀螺仪、加速度计、地磁场传感器&#xff0c;采用高性能的微处理器和先进的动力学解算与卡尔曼动态滤波算法&#xff0c;能够快速求解出模块当前的实时运…

【K8S基础】-k8s的核心概念控制器和调度器

Kubernetes是一个开源的容器编排平台&#xff0c;旨在简化和自动化容器化应用程序的部署、扩展和管理。它提供了一个强大的基础设施来管理容器化应用程序的生命周期&#xff0c;并确保它们在整个集群中高效运行。 Kubernetes的核心概念包括集群、节点、Pod、控制器、调度器等。…

360压缩安装一半不动了怎么办?

360压缩软件是我们常用的压缩软件&#xff0c;但是常常会遇到压缩安装到一半停止的情况&#xff0c;下面提供了一些可能的原因和解决办法&#xff0c;大家可以进行尝试~ 方法一&#xff1a;关闭防火墙和杀毒软件 有时候&#xff0c;防火墙和杀毒软件可能会阻止360压缩的安装过…