我是函数式编程的忠实粉丝,我喜欢它的简洁性,它更适合我的思维方式。 我也喜欢干净的代码,并尽可能减少冗余。 话虽如此,无分式(有时也称为默认)风格吸引我也就不足为奇了。 在过去的几天里,当我在JavaScript中应用此样式时遇到了一些陷阱,并决定将它们记下来。
什么是无点式
正如维基百科所述 :
[无点]是一种编程范例,在该范例中,函数定义不标识其操作所依据的参数(或“点”)。
乍一看这很奇怪,但让我们使用一个简单的示例。 假设我们有一个函数,它接受一个字符串并以首字母大写返回该字符串。 接下来,我们有一个我们都想大写的字符串数组。 这是map
函数的一个简单用例:
注意第二个map
用法,它没有声明参数的名称,也没有创建新的函数。 之所以起作用,是因为map
调用其第一个参数作为带有三个参数的函数:
- 要处理的数组项(这是唯一的必需参数),
- 该项目的索引,
- 整个数组正在处理
capitalise
功能恰好也将要处理的项目作为它的第一个(也是唯一一个)参数,因此在这种情况下使用无点时可以使用。
这种风格还有更多用途,我们在阅读本文时会看到它们。
难题1:函数接受的参数比您预期的要多
第一个陷阱来自于这样一个事实,即您可以在JavaScript中使用所需的任意数量的参数来调用函数-数量过多或过多。
如果您提供的参数太少,则未提供的参数将设置为其默认值(除非另有说明,否则undefined
)。
如果提供的参数过多,该函数将忽略过多的arguments
(除非它使用arguments
对象 )。
对于您而言,这可能并不新鲜,但是在无分的情况下,它可能会导致某些意外结果。
让我们以最简单的示例为例:编写一个函数,该函数采用字符串数组并返回项目的数值。 为了举例,我们假设输入是正确的。 很简单,这里有Number.parseFloat
:
正如我们所看到的,无点版本就像是一种魅力。
好吧,如果有人告诉我们数字总是整数并且我们不必将它们解析为浮点数呢? 然后,我们将Number.parseFloat
交换为Number.parseInt
,对吗?
哇,那是什么? 无点版本突然之间表现得很奇怪。
这样做的原因是,虽然Number.parseFloat
仅采用一个参数(要解析的字符串),但Number.parseInt
采用附加的可选参数–要输出的数字的基数(例如,十六进制字符串为16)。 因此,当在这样的地图中使用时,实际上是这样:
如我们所见, Number.parseInt
的基数参数是使用当前项目的索引设置的。 这就解释了11
输入的3
输出,因为3是二进制的11。
这是JavaScript的无指向性可能引起的第一类问题:函数所接受的参数比您期望的要多。
除了仅对您知道签名且不会更改的函数使用无点保护之外,没有其他方法可以保护自己免于此攻击,否则,代码可能会意外中断。
疑难杂症#2:意外this
我不久前接受的工作面试中突然出现了这个:
问题是要修复该错误。
可能有人会期望输出"Hello"
(我知道我做到了)。 但是, undefined
会输出到控制台。
原因是setTimeout
执行回调函数的方式。 该回调是在不同的执行上下文中执行 ,如果this
未明确设置,它会被设置为global
对象。 并且由于global
(或window
如果在浏览器中运行)没有message
成员,因此我们的示例undefied
输出。
有两种方法可以解决此问题:
第一个使用了闭包来隐式设置this
的的getMessage
调用适当的值。
第二(自由点)一个利用了的绑定方法来设置的值this
明确 。
还有另一个看起来不错的代码示例-简单的常规模式使用:
但是,这最终TypeError
了TypeError
说:
Method RegExp.prototype.test called on incompatible receiver undefined
或在Safari中更有用:
RegExp.prototype.test requires that |this| be an Object
再次,问题是, this
有意想不到的值(在这种情况下, undefined
)。 解决方案与前面的情况相同:
这里的要点是,如果要调用无点调用的函数使用了this
,则应该非常注意它已设置为期望的值。
结论
尽管无点样式在其他(功能)语言中很有用,但在JavaScript中,它常常带来一些问题,这些问题可能不值得它带来的简洁性。 当调用的函数在我的控制之下时,我有时仍会使用它。 经过这些经验之后,我会更加谨慎。
From: https://hackernoon.com/point-free-gotchas-in-javascript-d5c1f8fa5391