编程语言的究极问题:过程式还是函数式?
闭包是函数式编程最先引进的,基本假设就是所有量都是常量。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()
还是有副作用的呀,得到的值还是不一样,好处在哪?好处在于
-
bar
前面是什么?是一个对象state
,我们现在能确定bar
的副作用在哪里了; -
bar
本身没有副作用,只要我们深拷贝state
,我们就能历史回放state.bar()
是怎么出bug的。
以上两点简直是debug的福音。
怎么办
能减少bug,又实际的写法:
只有对象保存状态,不论是字面量,还是new
。函数可以读状态,写状态必须用this.xxx
或者用参数把对象传进来。
为什么说2018(其实2015+就行了)年就可以少用闭包了呢?
- 因为我们有
class
关键字了,以前创建用得上this
的类实在是太麻烦了; - 因为我们有箭头函数了,用闭包保存
this
的做法也不必要了。
当然,严格来说箭头函数也是一种闭包,因为它是函数和词法this
的组合。但是this
能保证只读,函数并不能用this
写状态,因此仍然满足上面说的两个好处。
所以直接说箭头函数就是闭包,我天天用,秀面试官一脸就行了,他不懂的话就潇洒离去,这破地方配不上你 :)
所以,React强推class
,Vue用的也是this
,都用对象存状态。既然状态都被对象存了,自然也就没闭包什么事了。
我的相关文章
Javascript闭包:从理论到实现,[[Scopes]]
的每一根毛都看得清清楚楚
以上所有代码按Mozilla Public License, v. 2.0授权。
以上所有文字内容按CC BY-NC-ND 4.0授权。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。