5
编程语言的究极问题:过程式还是函数式?

闭包是函数式编程最先引进的,基本假设就是所有量都是常量。Javascript想方设法糅合过程式与函数式两种风格,忽略了闭包的基本假设,于是造出天坑。

是什么

闭包的定义是“函数和声明该函数的词法环境的组合” MDN,换言之,就是带着环境(上下文、状态、属性、局部变量,找一个你能理解的词)的函数。从ES2015起,最简单的闭包变成了这样:

{
  let localVar = 1;
  whatever.onclick = () => localVar++;
}

whatever.onclick就是一个闭包,因为它带着localVar

从这个角度上来说,所有Javascript函数都是闭包,因为他们都能访问到window下面的全局变量,如果传递给另一个框架的话,用的都是自带的全局变量环境。

为什么

上面的闭包非常糟糕,糟糕就在于它不是纯函数它是有状态的。分散在各个闭包中的状态会成为bug的温床。以foo为例,每次调用都会有副作用,得到的值都不一样,状态在哪里又找不着(假设你忘了localVar是啥),给debug造成极大的困难。

现在主流的状态管理框架Flux/Redux/Vuex的思想全都是集中状态管理。还把状态分散到一个一个闭包里面,是过时的。

那我们把状态保存在哪里?this里。

虽然this也是JS的一个天坑,但是比起闭包来,简直好太多了。this最伟大的功勋就在于函数和环境的解耦(跟闭包正好相反)。

let bar = function() { this.a++ }
let state = { a: 1, bar };

这个时候有同学就要问了,state.bar()还是有副作用的呀,得到的值还是不一样,好处在哪?好处在于

  1. bar前面是什么?是一个对象state,我们现在能确定bar的副作用在哪里了;
  2. bar本身没有副作用,只要我们深拷贝state,我们就能历史回放state.bar()是怎么出bug的。

以上两点简直是debug的福音。

怎么办

能减少bug,又实际的写法:

只有对象保存状态,不论是字面量,还是new。函数可以读状态,写状态必须用this.xxx或者用参数把对象传进来。

为什么说2018(其实2015+就行了)年就可以少用闭包了呢?

  1. 因为我们有class关键字了,以前创建用得上this的类实在是太麻烦了;
  2. 因为我们有箭头函数了,用闭包保存this的做法也不必要了。

当然,严格来说箭头函数也是一种闭包,因为它是函数和词法this的组合。但是this能保证只读,函数并不能用this写状态,因此仍然满足上面说的两个好处。

所以直接说箭头函数就是闭包,我天天用,秀面试官一脸就行了,他不懂的话就潇洒离去,这破地方配不上你 :)

所以,React强推class,Vue用的也是this,都用对象存状态。既然状态都被对象存了,自然也就没闭包什么事了。

我的相关文章

Javascript闭包:从理论到实现,[[Scopes]]的每一根毛都看得清清楚楚

以上所有代码按Mozilla Public License, v. 2.0授权。
以上所有文字内容按CC BY-NC-ND 4.0授权。


liqi0816
1.9k 声望578 粉丝