DDR爱好者之家 Design By 杰米

本文实例讲述了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.jb51.net/code/HtmlJsRun测试上述代码运行效果。

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

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

DDR爱好者之家 Design By 杰米
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
DDR爱好者之家 Design By 杰米

《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线

暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。

艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。

《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。