JS箭头函数和常规函数之间的区别实例分析【 5 个区别】


Posted in Javascript onMay 27, 2020

本文实例讲述了JS箭头函数和常规函数之间的区别。分享给大家供大家参考,具体如下:

在 JavaScript 中,你可以通过多种方式去定义函数。

第一种常用的方法是使用关键字 function

// 函数声明
function greet(who) {
 return `Hello, ${who}!`;
}
// 函数表达式
const greet = function(who) {
 return `Hello, ${who}`;
}

代码中的函数声明和函数表达式被称为“常规函数”。

从 ES2015 开始,第二种可用的方法是 箭头函数 语法:

const greet = (who) => {
 return `Hello, ${who}!`;
}

虽然两者的语法都能够定义函数,但是在开发时该怎么选择呢?这是个好问题。

在本文中,我将展示两者之间的主要区别,以供你能够根据需要选择正确的语法。

1. this

1.1常规函数

在常规 JavaScript 函数内部,this 值(即执行上下文)是动态的。

动态上下文意味着 this 的值取决于如何调用函数。在 JavaScript 中,有 4 种调用常规函数的方式。

简单调用过程中,this 的值等于全局对象(如果函数在严格模式下运行,则为 undefined ):

function myFunction() {
 console.log(this);
}

// 简单调用
myFunction(); // logs global object (window)

方法调用过程中,this 的值是拥有该方法的对象:

const myObject = {
 method() {
  console.log(this);
 }
};
// 方法调用
myObject.method(); // logs "myObject"

在使用 myFunc.call(context, arg1, ..., argN)myFunc.apply(context, [arg1, ..., argN]) 的间接调用中,this 的值等于第一个参数:

function myFunction() {
 console.log(this);
}

const myContext = { value: 'A' };

myFunction.call(myContext); // logs { value: 'A' }
myFunction.apply(myContext); // logs { value: 'A' }

在使用关键字 new 的构造函数调用期间,this 等于新创建的实例:

function MyFunction() {
 console.log(this);
}

new MyFunction(); // logs an instance of MyFunction

1.2箭头函数

箭头函数中 this 的行为与常规函数的 this 行为有很大不同。

无论如何执行或在何处执行,箭头函数内部的 this 值始终等于外部函数的 this 值。换句话说,箭头函数可按词法解析 this,箭头函数没有定义自己的执行上下文。

在以下示例中,myMethod() 是箭头函数 callback() 的外部函数:

const myObject = {
 myMethod(items) {
  console.log(this); // logs "myObject"  
  const callback = () => {
   console.log(this); // logs "myObject"  
  };
  items.forEach(callback);
 }
};

myObject.myMethod([1, 2, 3]);

箭头函数 callback() 中的 this 值等于外部函数 myMethod()this

this 词法解析是箭头函数的重要功能之一。在方法内部使用回调时,要确保箭头函数没有定义自己的 this:不再有 const self = this 或者 callback.bind(this) 这种解决方法。

2.构造函数

2.1 常规函数

如上一节所述,常规函数可以轻松的构造对象。

例如用 Car() 函数创建汽车的实例:

function Car(color) {
 this.color = color;
}

const redCar = new Car('red');
redCar instanceof Car; // => true

Car 是常规函数,使用关键字 new 调用时会创建 Car 类型的新实例。

2.2 箭头函数

this 词法解决了箭头函数不能用作构造函数。

如果你尝试调用带有 new 关键字前缀的箭头函数,则 JavaScript 会引发错误:

const Car = (color) => {
 this.color = color;
};

const redCar = new Car('red'); // TypeError: Car is not a constructor

调用 new Car('red')(其中 Car 是箭头函数)会抛出 TypeError: Car is not a constructor

3. arguments 对象

3.1 常规函数

在常规函数的主体内部,arguments 是一个特殊的类似于数组的对象,其中包含被调用函数的参数列表。

让我们用 3 个参数调用 myFunction 函数:

function myFunction() {
 console.log(arguments);
}

myFunction('a', 'b'); // logs { 0: 'a', 1: 'b'}

类似于数组对象的 arguments 中包含调用参数:'a''b'

3.2箭头函数

另一方面,箭头函数内部未定义 arguments 特殊关键字。

用词法解析 arguments 对象:箭头函数从外部函数访问 arguments

让我们试着在箭头函数内部访问 arguments

function myRegularFunction() {
 const myArrowFunction = () => {  
   console.log(arguments); 
 }
 myArrowFunction('c', 'd');
}

myRegularFunction('a', 'b'); // logs { 0: 'a', 1: 'b' }

箭头函数 myArrowFunction() 由参数 'c', 'd' 调用。在其主体内部,arguments 对象等于调用 myRegularFunction() 的参数: 'a', 'b'

如果你想访问箭头函数的直接参数,可以使用剩余参数 ...args

function myRegularFunction() {
 const myArrowFunction = (...args) => {  
   console.log(args); 
 }
 myArrowFunction('c', 'd');
}

myRegularFunction('a', 'b'); // logs { 0: 'c', 1: 'd' }

剩余参数 ... args 接受箭头函数的执行参数:{ 0: 'c', 1: 'd' }

4.隐式返回

4.1常规函数

使用 return expression 语句从函数返回结果:

function myFunction() {
 return 42;
}

myFunction(); // => 42

如果缺少 return 语句,或者 return 语句后面没有表达式,则常规函数隐式返回 undefined

function myEmptyFunction() {
 42;
}

function myEmptyFunction2() {
 42;
 return;
}

myEmptyFunction(); // => undefined
myEmptyFunction2(); // => undefined

4.2箭头函数

可以用与常规函数相同的方式从箭头函数返回值,但有一个有用的例外。

如果箭头函数包含一个表达式,而你省略了该函数的花括号,则将显式返回该表达式。这些是内联箭头函数

const increment = (num) => num + 1;

increment(41); // => 42

increment() 仅包含一个表达式:num + 1。该表达式由箭头函数隐式返回,而无需使用 return 关键字。

5. 方法

5.1 常规函数

常规函数是在类上定义方法的常用方式。

在下面 Hero 类中,用了常规函数定义方法 logName()

class Hero {
 constructor(heroName) {
  this.heroName = heroName;
 }

 logName() {  console.log(this.heroName); }}

const batman = new Hero('Batman');

通常把常规函数用作方法。

有时你需要把该方法作为回调提供给 setTimeout() 或事件监听器。在这种情况下,你可能会很难以访问 this 的值。

例如用 logName() 方法作为 setTimeout() 的回调:

setTimeout(batman.logName, 1000);
// after 1 second logs "undefined"

1 秒钟后,undefined 会输出到控制台。 setTimeout()执行 logName 的简单调用(其中 this 是全局对象)。这时方法会与对象分离。

让我们手动把 this 值绑定到正确的上下文:

setTimeout(batman.logName.bind(batman), 1000);
// after 1 second logs "Batman"

batman.logName.bind(batman)this 值绑定到 batman 实例。现在,你可以确定该方法不会丢失上下文。

手动绑定 this 需要样板代码,尤其是在你有很多方法的情况下。有一种更好的方法:把箭头函数作为类字段。

5.2 箭头函数

感谢类字段提案(目前在第3阶段),你可以将箭头函数用作类中的方法。

与常规函数相反,现在用箭头定义的方法能够把 this 词法绑定到类实例。

让我们把箭头函数作为字段:

class Hero {
 constructor(heroName) {
  this.heroName = heroName;
 }

 logName = () => {  
   console.log(this.heroName); 
 }
}

const batman = new Hero('Batman');

现在,你可以把 batman.logName 用于回调而无需手动绑定 thislogName() 方法中 this 的值始终是类实例:

setTimeout(batman.logName, 1000);
// after 1 second logs "Batman"

6. 总结

了解常规函数和箭头函数之间的差异有助于为特定需求选择正确的语法。

常规函数中的 this 值是动态的,并取决于调用方式。是箭头函数中的 this 在词法上是绑定的,等于外部函数的 this

常规函数中的 arguments 对象包含参数列表。相反,箭头函数未定义 arguments(但是你可以用剩余参数 ...args 轻松访问箭头函数参数)。

如果箭头函数有一个表达式,则即使不用 return 关键字也将隐式返回该表达式。

最后一点,你可以在类内部使用箭头函数语法定义去方法。粗箭头方法将 this 值绑定到类实例。

不管怎样调用胖箭头方法,this 始终等于类实例,在回调这些方法用时非常有用。

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
JavaScipt基本教程之JavaScript语言的基础
Jan 16 Javascript
Javascript 调试利器 Firebug使用详解六
Jul 05 Javascript
JavaScript入门之事件、cookie、定时等
Oct 21 Javascript
node.js中的http.response.writeHead方法使用说明
Dec 14 Javascript
使用bootstrap实现多窗口和拖动效果
Sep 22 Javascript
基于chosen插件实现人员选择树搜索自动筛选功能
Sep 24 Javascript
vue实现可增删查改的成绩单
Oct 27 Javascript
在node中如何使用 ES6
Apr 22 Javascript
js实现随机点名小功能
Aug 17 Javascript
vue2.0中set添加属性后视图不能更新的解决办法
Feb 22 Javascript
jQuery 筛选器简单操作示例
Oct 02 jQuery
js回到页面指定位置的三种方式
Dec 17 Javascript
使用JavaScript获取Django模板指定键值数据
May 27 #Javascript
基于Vue CSR的微前端实现方案实践
May 27 #Javascript
Node.js API详解之 vm模块用法实例分析
May 27 #Javascript
jQuery实现鼠标滑动切换图片
May 27 #jQuery
js验证账户名是否重复
May 26 #Javascript
小程序富文本提取图片可放大缩小
May 26 #Javascript
微信小程序自定义联系人弹窗
May 26 #Javascript
You might like
PHP实现的mysql主从数据库状态检测功能示例
2017/07/20 PHP
用JavaScript实现单继承和多继承的简单方法
2009/03/29 Javascript
Node.js中创建和管理外部进程详解
2014/08/16 Javascript
扒一扒JavaScript 预解释
2015/01/28 Javascript
js中setTimeout()与clearTimeout()用法实例浅析
2015/05/12 Javascript
js实现汉字排序的方法
2015/07/23 Javascript
js生成随机数(指定范围)的实例代码
2016/07/10 Javascript
微信小程序实现城市列表选择
2018/06/05 Javascript
elementUI Vue 单个按钮显示和隐藏的变换功能(两种方法)
2018/09/04 Javascript
VUE DOM加载后执行自定义事件的方法
2018/09/07 Javascript
Bootstrap 时间日历插件bootstrap-datetimepicker配置与应用小结
2019/05/28 Javascript
vue+echarts实现动态折线图的方法与注意
2020/09/01 Javascript
[00:36]我的中国心——Serenity vs Fnatic
2018/08/21 DOTA
python采集微信公众号文章
2018/12/20 Python
对Python Class之间函数的调用关系详解
2019/01/23 Python
python使用Pandas库提升项目的运行速度过程详解
2019/07/12 Python
django框架forms组件用法实例详解
2019/12/10 Python
python获取依赖包和安装依赖包教程
2020/02/13 Python
使用Python发现隐藏的wifi
2020/03/04 Python
django处理select下拉表单实例(从model到前端到post到form)
2020/03/13 Python
python使用列表的最佳方案
2020/08/12 Python
GE设备配件:GE Appliance Parts(家电零件、配件和滤水器)
2018/11/28 全球购物
部队党性分析材料
2014/02/16 职场文书
股权转让意向书
2014/04/01 职场文书
《猴子种果树》教学反思
2014/04/26 职场文书
党课心得体会范文
2014/09/09 职场文书
群众路线对照检查材料
2014/09/22 职场文书
新生入学欢迎词
2015/01/26 职场文书
财务总监岗位职责
2015/02/03 职场文书
关于迟到的检讨书
2015/05/06 职场文书
2015中学教学工作总结
2015/07/22 职场文书
2016年秋季运动会加油稿
2015/12/21 职场文书
Python利器openpyxl之操作excel表格
2021/04/17 Python
OpenCV3.3+Python3.6实现图片高斯模糊
2021/05/18 Python
SpringDataJPA实体类关系映射配置方式
2021/12/06 Java/Android
【海涛DOTA】D-cup邀请赛NV.cn vs DT.Love
2022/04/01 DOTA