add by zhj: 最近在看ES6,看到了箭头函数,我个人感觉箭头函数适用于函数体中不用this的匿名函数,在箭头函数中使用this是一个坑

原文:http://ourjs.com/detail/584f83664edfe07ccdb23445

在ES6大行其道的今天,不应用点ES6特性似乎有些政治不正确。最近刚好有个Node的项目,最低要支持到nodejs 4.0,在node.green看了下ES6的支持度,我想使用的特性基本都有支持,遂决定在新项目中采用ES6来写。

当然第一件事情就是毫不留情地消灭var,项目中能用const的地方不用let,能用let的地方不用var。

第二件事情就是使用劳动人民喜闻乐见的箭头函数替代function。当我心满意足地看到满屏的=>时,现实给了我一记响亮的耳光——改过之后的程序错误百出!

所以,当我们使用箭头函数时,一定要搞清楚箭头函数是什么回事,适用于什么场景。本文就针对以上问题来讨论下箭头函数。

箭头函数是什么?

箭头函数的语法我就不讲了,相信大家都见识过。跟我一样,大家喜欢箭头函数90%的原因是它好看。除了好看,它是不是与function等价呢?肯定不等价,因为TC39不可能仅因为好看而引入一个语法糖(class除外)。

箭头函数的渊源可以追溯到上古时期一个叫lambda演算的东西。lambda演算是数学家提出来的,有些数学家跟我们程序员一样也很懒,数学定理那么多,今天要证三角定律,明天要证勾股定律,累不累!那能不能将所有的证明问题用一个统一的体系进行形式化描述,然后由机器来完成自动推导呢?lambda演算就是干这个的,图灵也搞了一套体系叫图灵机,两者是等价的。

关于lambda演算说了这么多,好像跟今天要讲的箭头函数没什么关系?其实是有关系的,lambda演算深刻影响了箭头函数的设计。数学家们喜欢用纯函数式编程语言,纯函数的特点是没有副作用,给予特定的输入,总是产生确定的输出,甚至有些情况下通过输出能够反推输入。要实现纯函数,必须使函数的执行过程不依赖于任何外部状态,整个函数就像一个数学公式,给定一套输入参数,不管是在地球上还是火星上执行都是同一个结果。

箭头函数要实现类似纯函数的效果,必须剔除外部状态。所以当你定义一个箭头函数,在普通函数里常见的thisargumentscaller是统统没有的。

箭头函数没有this

箭头函数没有this,那下面的代码明显可以取到this啊:

function foo() {
this.a = 1
let b = () => console.log(this.a) b()
} foo() //

以上箭头函数中的this其实是父级作用域中的this,即函数foothis。箭头函数引用了父级的变量,构成了一个闭包。以上代码等价于:

function foo() {
this.a = 1 let self = this
let b = () => console.log(self.a) b()
} foo() //

箭头函数不仅没有this,常用的arguments也没有。如果你能获取到arguments,那它一定是来自父作用域的。

function foo() {
return () => console.log(arguments[0])
} foo(1, 2)(3, 4) //

上例中如果箭头函数有arguments,就应该输出的是3而不是1。

一个经常犯的错误是使用箭头函数定义对象的方法,如:

let a = {
foo: 1,
bar: () => console.log(this.foo)
} a.bar() //undefined

以上代码中,箭头函数中的this并不是指向a这个对象。对象a并不能构成一个作用域,所以再往上到达全局作用域,this就指向全局作用域。如果我们使用普通函数的定义方法,输出结果就符合预期,这是因为a.bar()函数执行时作用域绑定到了a对象。

let a = {
foo: 1,
bar: function() { console.log(this.foo) }
} a.bar() //

另一个错误是在原型上使用箭头函数,如:

function A() {
this.foo = 1
} A.prototype.bar = () => console.log(this.foo) let a = new A()
a.bar() //undefined

同样,箭头函数中的this不是指向A,而是根据变量查找规则回溯到了全局作用域。同样,使用普通函数就不存在问题。

通过以上说明,我们可以看出,箭头函数除了传入的参数之外,真的是什么都没有!如果你在箭头函数引用了thisarguments或者参数之外的变量,那它们一定不是箭头函数本身包含的,而是从父级作用域继承的。

什么情况下该使用箭头函数

到这里,我们可以发现箭头函数并不是万金油,稍不留神就会踩坑。

至于什么情况该使用箭头函数,《You Don’t Know JS》给出了一个决策图: 

以上决策图看起来有点复杂,我认为有三点比较重要:

  1. 箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如用在mapreducefilter的回调函数定义中;
  2. 不要在最外层定义箭头函数,因为在函数内部操作this会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将this控制在可见的范围内;
  3. 如开头所述,箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数。

JS中lambda表达式的优缺点和使用场景(转)的更多相关文章

  1. Java中lambda表达式详解

    原文地址:http://blog.laofu.online/2018/04/20/java-lambda/ 为什么使用lambda 在java中我们很容易将一个变量赋值,比如int a =0;int ...

  2. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...

  3. Python 中Lambda 表达式 实例解析

    Lambda 表达式 lambda表达式是一种简洁格式的函数.该表达式不是正常的函数结构,而是属于表达式的类型.而且它可以调用其它函数. 1.基本格式: lambda 参数,参数...:函数功能代码 ...

  4. VS编译环境中TBB配置和C++中lambda表达式

    TBB(Thread Building Blocks),线程构建模块,是由Intel公司开发的并行编程开发工具,提供了对Windows,Linux和OSX平台的支持. TBB for Windows ...

  5. Java8 Collections.sort()及Arrays.sort()中Lambda表达式及增强版Comparator的使用

    摘要:本文主要介绍Java8 中Arrays.sort()及Collections.sort()中Lambda表达式及增强版Comparator的使用. 不废话直接上代码 import com.goo ...

  6. Spark中Lambda表达式的变量作用域

    通常,我们希望能够在lambda表达式的闭合方法或类中访问其他的变量,例如: package java8test; public class T1 { public static void main( ...

  7. python中lambda表达式应用

    对于简单的函数,也存在一种简便的表示方式,即:lambda表达式 #普通函数1 def func(a): return a+1 print 'test1_func0:',func(1000)4#lam ...

  8. Python中lambda表达式学习

    lambda只是一个表达式,函数体比def简单很多. lambda的主体是一个表达式,而不是一个代码块.仅仅能在lambda表达式中封装有限的逻辑进去. lambda表达式是起到一个函数速写的作用.允 ...

  9. java 8 中lambda表达式学习

    转自 http://blog.csdn.net/renfufei/article/details/24600507 http://www.jdon.com/idea/java/10-example-o ...

随机推荐

  1. java计算某个坐标是否在范围内

    java电子围栏 圆.矩形.多边形算法 http://blog.csdn.net/deepak192/article/details/79402694/ java-经纬度有关的计算(半径内的经纬度范围 ...

  2. iOS CrashLog Analysis

    链接: iOS友盟崩溃日志定位代码 [友盟统计报表解读]之错误分析iOS版 DYSMTool Download Address 利用友盟和dsym分析发布app用户错误 iOS -- 友盟工具进行Cr ...

  3. iptables设置端口转发

    转自:https://blog.csdn.net/sigangjun/article/details/17412821 一 从一台机到另一台机端口转发 启用网卡转发功能 #echo 1 > /p ...

  4. 使用BFG移除git库中的大文件或污点提交

  5. [Java] 简化正则表达式的使用

    使用 RegexString.with(string).pattern(pattern).start() + 后续操作(matches,find或者是replace) 源码 package com; ...

  6. .NetCore中EFCore的使用整理(二)-关联表查询

    EF常用处理关联加载的方式有3中:延迟加载(Lazy Loading).贪婪加载 (Eager Loading)以及显示加载. 一.EF Core  1.1 1.当前的版本,还不支持延迟加载(Lazy ...

  7. CSS网页布局垂直居中整理

    一.使用CSS3处理垂直居中方式 1.使用Flex布局处理(推荐),简单好用 body,html{ width:100%; height:100%; } .out { width: 20%; heig ...

  8. 【转】Angular之constructor和ngOnInit差异及适用场景

    原文:http://liuwenzhuang.github.io/2016/03/04/angular2-constructor-versus-ngOnInit.html -------------- ...

  9. Cannot generate C# proxy dll with JNI4NET tool, running batch file as trusted assembly?

    From: https://stackoverflow.com/questions/41042368/cannot-generate-c-sharp-proxy-dll-with-jni4net-to ...

  10. C++中的extern

    这篇文章解释的简单明了: https://stackoverflow.com/questions/10422034/when-to-use-extern-in-c This comes in usef ...